はじめに
Node.jsは、高速でスケーラブルなサーバーサイド開発を可能にする人気のプラットフォームです。そして、そのNode.jsをさらに活用できる強力なWebフレームワークが「Express」です。本記事では、Expressを使ってWebアプリケーションを開発する方法を初心者向けに解説します。これから始める方や、まだExpressに触れたことがない方でも安心して学べる内容となっています。まずはExpressのセットアップから、ルーティング、API作成の基本的な部分までを順を追って解説し、実践的な知識を身につけていきましょう。
今回のゴール
ExpressサーバーにGET, POSTメソッドでアクセスしてレスポンスが返ってくることを確認します。
サーバーへのリクエストはcurl
コマンドを用いて行います。
ルーティング以外にも
- クエリパラメータがセットされたときの処理方法
- ミドルウェアを使ってアクセスログを取得
- 共通のエラー処理
なども実装しより現場で活用できる内容まで深掘りしていきます。
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開発スキルを磨いていきましょう。
関連する技術ブログ
GraphQL・REST API の堅牢な認可設計:RBAC・ABAC・OAuth 2.0 のベストプラクティス
GraphQL & REST API の堅牢な認可設計を構築する方法とは?RBAC・ABAC の活用、Rate Limiting、API Gateway、監視のベストプラクティスをまとめました。
shinagawa-web.com
キャッシュ戦略完全ガイド:CDN・Redis・API最適化でパフォーマンスを最大化
Webアプリの高速化には、適切なキャッシュ戦略が不可欠。本記事では、CDN(Cloudflare / AWS CloudFront)による静的コンテンツ配信、Redis / Memcached を活用したデータベース負荷軽減、APIレスポンスキャッシュ(GraphQL / REST API)など、キャッシュを駆使してパフォーマンスを向上させる方法を解説。TTL設定、Next.js ISR / SSG のプリフェッチ、クエリキャッシュ(React Query / Apollo Client)、キャッシュヒット率の分析など、実践的なノウハウも紹介します。
shinagawa-web.com
ExpressとMongoDBで簡単にWeb APIを構築する方法【TypeScript対応】
本記事では、MongoDB Atlasを活用してREST APIを構築する方法を、初心者の方にも分かりやすいステップで解説します。プロジェクトの初期設定からMongoDBの接続設定、Expressを使用したルートの作成、さらにTypeScriptを用いた型安全な実装まで、実践的なサンプルコードを交えて丁寧に説明します。
shinagawa-web.com
マイクロサービス戦略の実践:BFF・API管理・認証基盤を支える技術スタック(AWS, Keycloak, gRPC, Kafka)
マイクロサービス化を進める上で、適切なアーキテクチャ設計と技術選定が重要です。本記事では、BFF の導入計画、API Gateway を活用したエンドポイント管理、認証・認可の統合、非同期メッセージング、サービス間通信の最適化 など、多岐にわたるトピックを解説します。サンプルとして、AWS の活用例、Keycloak による認証基盤の整備、gRPC を用いた高速通信、Kafka & RabbitMQ によるメッセージング、Swagger での API ドキュメント標準化 などを紹介。マイクロサービスを支える技術スタックとその実践的な活用方法を学びたい方におすすめの内容です。
shinagawa-web.com
フロントエンド開発に役立つモックサーバー構築:@graphql-tools/mock と Faker を使った実践ガイド
フロントエンド開発を先行させるために、バックエンドが未完成でもモックサーバーを立ち上げる方法を解説。@graphql-tools/mock と Faker を使用して、実際のデータに近い動作をシミュレートします。
shinagawa-web.com
React + Express を活用したモノレポ環境構築ガイド:Turborepoで効率的な開発を実現
React、Expressを使ってモノレポ環境を構築し、Turborepoでの効率的な開発を実現する方法を解説します。フロントエンドとバックエンドを統合した最適な開発環境を整えるためのステップを順を追って紹介します。
shinagawa-web.com
スキーマ駆動開発の実践:React × Express × GraphQLで効率的なAPI設計を実現
GraphQLを活用したスキーマ駆動開発(Schema-Driven Development, SDD)の実践方法を解説します。React(フロントエンド)とExpress(バックエンド)を組み合わせ、GraphQLスキーマを基にAPI設計を進めることで、型安全性と開発効率を向上させる手法を紹介します。GraphQL Code Generatorを用いた型定義の自動生成、スキーマ設計のベストプラクティス、Turborepoによるモノレポ構成についても詳しく解説します。
shinagawa-web.com
Supertest と Jest を活用した Express + MongoDB アプリのエンドツーエンドテスト解説
Supertest と Jest を組み合わせて Express アプリケーションの API テストを効率化する方法を解説します。サービス層の導入やテスト可能なコード設計へのリファクタリング、GET, POST, PATCH, DELETEメソッドのテスト実装まで、具体的なコード例を交えて詳しく紹介します。
shinagawa-web.com
弊社の技術支援サービス
無駄なコストを削減し、投資対効果を最大化する
クラウド費用の高騰、不要なSaaSの乱立、開発工数の増加――これらの課題に悩んでいませんか?本サービスでは、クラウドコストの最適化、開発効率向上、技術選定の最適化 を通じて、単なるコスト削減ではなく、ROIを最大化する最適解 をご提案します。
shinagawa-web.com
最新技術の導入・検証を支援するPoCサービス
Remix、React Server Components、TypeScript移行、クラウドサービス比較、マイクロサービス、サーバーレス、デザインシステムなど、最新技術のPoC(概念実証)を通じて、最適な技術選定と導入を支援します。貴社の開発課題に合わせた検証・実装で、ビジネスの成長を加速させます。
shinagawa-web.com
開発生産性を最大化するための支援サービス
開発チームの生産性向上、コードの品質管理、インフラの最適化まで、様々な側面からサポートします。コードベースのリファクタリングから、テスト自動化、オンボーディング強化まで、プロジェクトの成功に必要なすべての支援を提供。御社の開発現場が効率的に機能するように、技術的な障害を取り除き、スムーズな開発を実現します。
shinagawa-web.com
開発品質向上支援 – 効率的で安定したプロダクトを実現
フロントエンドからバックエンド、データベースまで、開発プロセス全体を最適化し、安定したプロダクト作りをサポートします。コードレビューの仕組み、型定義の強化、E2Eテスト環境の構築など、開発の各ステップにおけるベストプラクティスを導入することで、より効率的でバグの少ない、そしてユーザー満足度の高いサービス提供を支援します。
shinagawa-web.com
Webアプリのセキュリティ強化支援
Webアプリの脆弱性対策からインフラのセキュリティ強化まで、包括的なセキュリティ支援を提供。OWASP Top 10対策、JWT認証の最適化、APIのアクセス制御、依存パッケージの監査、セキュアコーディングの標準化など、実践的なアプローチで開発現場の安全性を向上させます。
shinagawa-web.com
目次
お問い合わせ