JSON、API、RESTサービス
REST APIでJSONを使用する方法。APIデザイン、HTTP メソッド、ステータスコード、認証、ベストプラクティス。
Big JSON Team
• Technical WriterExpert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.
# JSON、API、RESTサービス
JSONはREST APIのデータ交換フォーマットとして業界標準になっています。
REST APIとは
REST(Representational State Transfer)は、Webサービスを構築するためのアーキテクチャスタイルです。
REST の原則
HTTPメソッド
CRUD操作
| メソッド | 操作 | 説明 |
|---------|------|------|
| GET | Read | リソースの取得 |
| POST | Create | 新しいリソースの作成 |
| PUT | Update | リソース全体の更新 |
| PATCH | Update | リソースの部分更新 |
| DELETE | Delete | リソースの削除 |
GET - データの取得
GET /api/users
GET /api/users/123
GET /api/users?role=admin&limit=10
レスポンス:
{
"data": [
{
"id": 1,
"name": "田中太郎",
"email": "tanaka@example.com"
}
],
"total": 1,
"page": 1
}
POST - データの作成
POST /api/users
Content-Type: application/json
{
"name": "田中太郎",
"email": "tanaka@example.com",
"role": "user"
}
レスポンス:
{
"success": true,
"data": {
"id": 123,
"name": "田中太郎",
"email": "tanaka@example.com",
"role": "user",
"created": "2026-01-16T10:00:00Z"
}
}
PUT - 完全な更新
PUT /api/users/123
Content-Type: application/json
{
"name": "田中太郎",
"email": "newemail@example.com",
"role": "admin"
}
PATCH - 部分的な更新
PATCH /api/users/123
Content-Type: application/json
{
"role": "admin"
}
DELETE - 削除
DELETE /api/users/123
レスポンス:
{
"success": true,
"message": "ユーザーが削除されました"
}
HTTPステータスコード
成功レスポンス (2xx)
- 200 OK - 成功(GET、PUT、PATCH)
- 201 Created - リソースが作成された(POST)
- 204 No Content - 成功、コンテンツなし(DELETE)
クライアントエラー (4xx)
- 400 Bad Request - 無効なリクエスト
- 401 Unauthorized - 認証が必要
- 403 Forbidden - アクセス拒否
- 404 Not Found - リソースが見つからない
- 422 Unprocessable Entity - 検証エラー
サーバーエラー (5xx)
- 500 Internal Server Error - サーバーエラー
- 502 Bad Gateway - ゲートウェイエラー
- 503 Service Unavailable - サービス利用不可
レスポンス構造
成功レスポンス
{
"success": true,
"data": {
"id": 123,
"name": "田中太郎"
},
"message": "操作が成功しました"
}
エラーレスポンス
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "入力データが無効です",
"details": [
{
"field": "email",
"message": "有効なメールアドレスを入力してください"
}
]
}
}
ページネーション
{
"data": [...],
"pagination": {
"page": 1,
"limit": 10,
"total": 100,
"pages": 10
}
}
JavaScriptでのAPI使用
Fetch API
// GET リクエスト
async function getUsers() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error(HTTP error! status: ${response.status});
}
const data = await response.json();
return data;
} catch (error) {
console.error('エラー:', error);
throw error;
}
}
// 使用
const users = await getUsers();
console.log(users);
POST リクエスト
async function createUser(userData) {
try {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${token}
},
body: JSON.stringify(userData)
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error.message);
}
return data;
} catch (error) {
console.error('作成エラー:', error);
throw error;
}
}
// 使用
const newUser = {
name: '田中太郎',
email: 'tanaka@example.com'
};
const result = await createUser(newUser);
PUT/PATCH リクエスト
async function updateUser(userId, updates) {
const response = await fetch(https://api.example.com/users/${userId}, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updates)
});
return await response.json();
}
// 使用
await updateUser(123, { role: 'admin' });
DELETE リクエスト
async function deleteUser(userId) {
const response = await fetch(https://api.example.com/users/${userId}, {
method: 'DELETE'
});
if (response.status === 204) {
return { success: true };
}
return await response.json();
}
Node.js/Express での API作成
基本的なセットアップ
const express = require('express');
const app = express();
app.use(express.json()); // JSONパース
// データベース(例)
let users = [
{ id: 1, name: '田中太郎', email: 'tanaka@example.com' },
{ id: 2, name: '佐藤花子', email: 'sato@example.com' }
];
// GET - すべてのユーザー
app.get('/api/users', (req, res) => {
res.json({
success: true,
data: users
});
});
// GET - 特定のユーザー
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({
success: false,
error: { message: 'ユーザーが見つかりません' }
});
}
res.json({
success: true,
data: user
});
});
// POST - ユーザー作成
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
// バリデーション
if (!name || !email) {
return res.status(400).json({
success: false,
error: { message: '名前とメールは必須です' }
});
}
const newUser = {
id: users.length + 1,
name,
email
};
users.push(newUser);
res.status(201).json({
success: true,
data: newUser
});
});
// PUT - ユーザー更新
app.put('/api/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const index = users.findIndex(u => u.id === userId);
if (index === -1) {
return res.status(404).json({
success: false,
error: { message: 'ユーザーが見つかりません' }
});
}
users[index] = {
id: userId,
...req.body
};
res.json({
success: true,
data: users[index]
});
});
// DELETE - ユーザー削除
app.delete('/api/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const index = users.findIndex(u => u.id === userId);
if (index === -1) {
return res.status(404).json({
success: false,
error: { message: 'ユーザーが見つかりません' }
});
}
users.splice(index, 1);
res.status(204).send();
});
app.listen(3000, () => {
console.log('サーバーがポート3000で起動しました');
});
認証と認可
JWT (JSON Web Token)
const jwt = require('jsonwebtoken');
// トークン生成
function generateToken(user) {
return jwt.sign(
{ id: user.id, email: user.email },
'your-secret-key',
{ expiresIn: '24h' }
);
}
// ログイン
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
// ユーザー検証(例)
const user = await findUser(email, password);
if (!user) {
return res.status(401).json({
success: false,
error: { message: '認証に失敗しました' }
});
}
const token = generateToken(user);
res.json({
success: true,
data: {
token,
user: {
id: user.id,
name: user.name,
email: user.email
}
}
});
});
// 認証ミドルウェア
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({
success: false,
error: { message: 'トークンが必要です' }
});
}
try {
const decoded = jwt.verify(token, 'your-secret-key');
req.user = decoded;
next();
} catch (error) {
res.status(401).json({
success: false,
error: { message: '無効なトークンです' }
});
}
}
// 保護されたルート
app.get('/api/profile', authenticate, (req, res) => {
res.json({
success: true,
data: req.user
});
});
エラーハンドリング
グローバルエラーハンドラー
// エラーハンドリングミドルウェア
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
success: false,
error: {
message: err.message || '内部サーバーエラー',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
});
カスタムエラークラス
class ApiError extends Error {
constructor(message, status = 500) {
super(message);
this.status = status;
}
}
// 使用
app.get('/api/users/:id', async (req, res, next) => {
try {
const user = await findUserById(req.params.id);
if (!user) {
throw new ApiError('ユーザーが見つかりません', 404);
}
res.json({ success: true, data: user });
} catch (error) {
next(error);
}
});
CORS(クロスオリジンリソース共有)
const cors = require('cors');
// すべてのオリジンを許可
app.use(cors());
// 特定のオリジンのみ許可
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true
}));
レート制限
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 60 1000, // 15分
max: 100, // 最大100リクエスト
message: {
success: false,
error: { message: 'リクエストが多すぎます' }
}
});
app.use('/api/', limiter);
API のバージョニング
URLバージョニング
app.use('/api/v1/users', usersV1Router);
app.use('/api/v2/users', usersV2Router);
ヘッダーバージョニング
app.use((req, res, next) => {
const version = req.headers['api-version'] || '1';
req.apiVersion = version;
next();
});
ベストプラクティス
1. 一貫したレスポンス構造
// 良い
{
"success": true,
"data": {...}
}
// 悪い(不整合)
// 時々 { "data": ... }
// 時々 { "result": ... }
2. 適切なHTTPメソッド使用
// 良い
GET /api/users - ユーザーリスト取得
POST /api/users - ユーザー作成
// 悪い
GET /api/createUser - GETで作成は不適切
3. リソース名は複数形
// 良い
GET /api/users
GET /api/users/123
// 悪い
GET /api/user
GET /api/getUser/123
4. クエリパラメータでフィルタリング
GET /api/users?role=admin&status=active&limit=10&page=1
5. 適切なステータスコード
app.post('/api/users', (req, res) => {
const user = createUser(req.body);
res.status(201).json({ success: true, data: user }); // 201 Created
});
app.delete('/api/users/:id', (req, res) => {
deleteUser(req.params.id);
res.status(204).send(); // 204 No Content
});
6. ドキュメント化
Swagger/OpenAPIでAPIをドキュメント化:
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users:
get:
summary: ユーザーリスト取得
responses:
'200':
description: 成功
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
data:
type: array
items:
$ref: '#/components/schemas/User'
テスト
Jest でのAPI テスト
const request = require('supertest');
const app = require('./app');
describe('User API', () => {
test('GET /api/users should return users', async () => {
const response = await request(app)
.get('/api/users')
.expect(200)
.expect('Content-Type', /json/);
expect(response.body.success).toBe(true);
expect(Array.isArray(response.body.data)).toBe(true);
});
test('POST /api/users should create user', async () => {
const newUser = {
name: '田中太郎',
email: 'tanaka@example.com'
};
const response = await request(app)
.post('/api/users')
.send(newUser)
.expect(201);
expect(response.body.success).toBe(true);
expect(response.body.data.name).toBe(newUser.name);
});
});
まとめ
クイックリファレンス
// GET
fetch('/api/users')
.then(r => r.json())
.then(data => console.log(data));
// POST
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '田中太郎' })
});
// Express API
app.get('/api/users', (req, res) => {
res.json({ success: true, data: users });
});
app.post('/api/users', (req, res) => {
const user = createUser(req.body);
res.status(201).json({ success: true, data: user });
});
REST APIとJSONで、スケーラブルなWebサービスを構築しましょう!