Node.js × Fastify で爆速 REST API:スキーマ駆動とプラグイン設計を学ぶ

Node.js でバックエンド API を作りたいけれど、Express では物足りないと感じたことはありませんか?Fastify は高速性とモダンな設計思想で注目を集めている Web フレームワークです。本記事では、Fastify の最大の特徴である「スキーマ駆動開発」と「プラグインアーキテクチャ」を中心に、実践的な REST API の構築方法をご紹介します。初めて Fastify に触れる方でも理解できるよう、段階的に解説していきますね。
背景
Fastify とは
Fastify は Node.js 向けの高速な Web フレームワークで、2016 年に誕生しました。Express の後継として設計され、パフォーマンス、開発者体験、そして型安全性を重視しています。
以下の図は、Fastify の基本アーキテクチャを示しています。
mermaidflowchart TB
request["HTTPリクエスト"] --> router["ルーター"]
router --> validation["スキーマ検証"]
validation --> handler["ハンドラー"]
handler --> serialization["レスポンス<br/>シリアライズ"]
serialization --> response["HTTPレスポンス"]
schema["JSONスキーマ"] -.->|定義| validation
schema -.->|定義| serialization
plugins["プラグイン"] -.->|拡張| router
plugins -.->|拡張| handler
図の要点として、リクエストはスキーマ検証を経てハンドラーで処理され、レスポンスもスキーマに基づいてシリアライズされます。プラグインがすべての処理を拡張可能にしているのが特徴です。
Fastify が選ばれる理由
Fastify が多くの開発者に支持される理由を表にまとめました。
# | 特徴 | 説明 |
---|---|---|
1 | 高速性 | Express の約 2 倍のスループットを実現 |
2 | スキーマ駆動 | JSON Schema による型安全なバリデーション |
3 | プラグイン設計 | カプセル化されたモジュール構造 |
4 | TypeScript サポート | 型定義が完備され開発効率が向上 |
5 | 非同期優先 | async/await をネイティブサポート |
これらの特徴により、大規模なプロジェクトでも保守性を保ちながら高速な API を構築できます。
スキーマ駆動開発のメリット
スキーマ駆動開発とは、API の入出力を JSON Schema で定義し、それに基づいて検証とドキュメント生成を自動化する手法です。
mermaidflowchart LR
define["スキーマ定義"] --> validate["自動バリデーション"]
define --> serialize["高速シリアライズ"]
define --> docs["API<br/>ドキュメント生成"]
define --> types["TypeScript<br/>型生成"]
validate --> safety["型安全性"]
serialize --> performance["パフォーマンス"]
docs --> dx["開発者体験"]
types --> dx
スキーマを一度定義すれば、バリデーション、シリアライゼーション、ドキュメント生成が自動化され、開発効率が劇的に向上するのです。
課題
Express の限界
従来の Express では、以下のような課題が顕在化していました。
# | 課題 | 内容 |
---|---|---|
1 | パフォーマンス | シングルスレッドでの処理限界 |
2 | バリデーション | 手動実装が必要で型安全性が低い |
3 | レスポンス最適化 | JSON シリアライズが非効率 |
4 | プラグイン管理 | グローバルスコープによる依存関係の複雑化 |
5 | TypeScript 対応 | 型定義の不完全性 |
特に、リクエストのバリデーションを手動で行うと、コードが冗長になり保守性が低下します。また、エラーハンドリングも統一しにくいという問題がありました。
型安全性の欠如
Express では、リクエストパラメータやボディの型が実行時まで保証されません。
typescript// Express での典型的な問題
app.post('/users', (req, res) => {
// req.body.name が存在するか?string か?
// 実行時までわからない
const name = req.body.name;
// ...
});
このコードでは、req.body.name
の型が不明で、実行時エラーのリスクが高まります。開発時にバグを検出できないのは大きな課題ですね。
パフォーマンスボトルネック
Express は以下の点でパフォーマンス上の制約があります。
- JSON のシリアライズが遅い
- ルーティングのマッチングが非効率
- ミドルウェアのオーバーヘッド
大量のリクエストを処理する際、これらがボトルネックとなり、サーバーリソースを圧迫してしまうのです。
解決策
Fastify によるスキーマ駆動開発
Fastify は JSON Schema を活用し、型安全で高速な API を実現します。
mermaidflowchart TB
schema_def["JSON Schema<br/>定義"] --> compile["スキーマ<br/>コンパイル"]
compile --> ajv["AJV<br/>バリデーター"]
compile --> fast_json["fast-json-stringify<br/>シリアライザー"]
request["リクエスト"] --> ajv
ajv -->|OK| handler["ハンドラー"]
ajv -->|NG| error["400 Error"]
handler --> result["結果"]
result --> fast_json
fast_json --> response["レスポンス"]
スキーマはコンパイルされて高速なバリデーターとシリアライザーに変換されます。この仕組みにより、実行時のパフォーマンスが大幅に向上するのです。
プラグインによるモジュール化
Fastify のプラグインシステムは、依存関係を明確にし、カプセル化を実現します。
mermaidflowchart TB
root["ルートインスタンス"]
root --> plugin_a["プラグイン A"]
root --> plugin_b["プラグイン B"]
plugin_a --> child_a1["子プラグイン A1"]
plugin_a --> child_a2["子プラグイン A2"]
plugin_b --> child_b1["子プラグイン B1"]
subgraph scope_a["スコープ A"]
plugin_a
child_a1
child_a2
end
subgraph scope_b["スコープ B"]
plugin_b
child_b1
end
各プラグインは独自のスコープを持ち、他のプラグインに影響を与えません。これにより、大規模なアプリケーションでも依存関係を管理しやすくなります。
具体例
プロジェクトのセットアップ
まず、Fastify プロジェクトを作成しましょう。
bashmkdir fastify-rest-api
cd fastify-rest-api
yarn init -y
必要なパッケージをインストールします。
bashyarn add fastify
yarn add -D typescript @types/node
yarn add -D ts-node nodemon
TypeScript の設定ファイルを作成します。
json{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
この設定で、TypeScript の厳格な型チェックを有効にし、安全なコードを書けるようにしています。
基本的な Fastify サーバー
シンプルな Fastify サーバーを作成してみましょう。
typescript// src/server.ts
import Fastify from 'fastify';
// Fastify インスタンスを作成
const fastify = Fastify({
logger: true, // ロガーを有効化
});
このコードで Fastify のインスタンスを作成しています。logger: true
により、リクエストやエラーのログが自動的に出力されます。
基本的なルートを追加します。
typescript// ヘルスチェックエンドポイント
fastify.get('/health', async (request, reply) => {
return {
status: 'ok',
timestamp: new Date().toISOString(),
};
});
サーバーを起動する処理を追加しましょう。
typescript// サーバー起動
const start = async () => {
try {
await fastify.listen({ port: 3000, host: '0.0.0.0' });
console.log(
'Server is running on http://localhost:3000'
);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();
この構造により、エラーが発生した場合も適切にログ出力され、プロセスが終了します。
スキーマ駆動の REST API
ユーザー管理 API をスキーマ駆動で実装してみます。
スキーマ定義
まず、ユーザーのスキーマを定義します。
typescript// src/schemas/user.schema.ts
// ユーザーオブジェクトのスキーマ
export const userSchema = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
name: { type: 'string', minLength: 1, maxLength: 100 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0, maximum: 150 },
},
required: ['id', 'name', 'email'],
} as const;
このスキーマでは、各フィールドの型と制約を明確に定義しています。as const
により TypeScript の型推論が強化されます。
POST リクエスト用のスキーマを定義します。
typescript// ユーザー作成リクエストのスキーマ
export const createUserSchema = {
body: {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 1,
maxLength: 100,
},
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0, maximum: 150 },
},
required: ['name', 'email'],
},
} as const;
レスポンスのスキーマも定義しておきます。
typescript// レスポンススキーマ
export const createUserResponseSchema = {
response: {
201: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: userSchema,
},
},
},
} as const;
このように、リクエストとレスポンスの両方をスキーマで定義することで、API の契約が明確になりますね。
TypeScript 型定義
スキーマから TypeScript の型を生成します。
typescript// src/types/user.types.ts
import { FromSchema } from 'json-schema-to-ts';
import {
userSchema,
createUserSchema,
} from '../schemas/user.schema';
// スキーマから型を生成
export type User = FromSchema<typeof userSchema>;
export type CreateUserBody = FromSchema<
typeof createUserSchema.body
>;
json-schema-to-ts
パッケージを使うことで、スキーマと型が完全に同期します。
bashyarn add json-schema-to-ts
ルートハンドラーの実装
ユーザー作成エンドポイントを実装しましょう。
typescript// src/routes/users.routes.ts
import {
FastifyInstance,
FastifyRequest,
FastifyReply,
} from 'fastify';
import { randomUUID } from 'crypto';
import { CreateUserBody, User } from '../types/user.types';
import {
createUserSchema,
createUserResponseSchema,
} from '../schemas/user.schema';
インメモリでユーザーを保存するストレージを用意します。
typescript// 簡易的なデータストア(本番環境ではDBを使用)
const users: User[] = [];
ユーザー作成ハンドラーを実装します。
typescript// ユーザー作成ハンドラー
async function createUserHandler(
request: FastifyRequest<{ Body: CreateUserBody }>,
reply: FastifyReply
) {
const { name, email, age } = request.body;
// 新規ユーザーを作成
const newUser: User = {
id: randomUUID(),
name,
email,
...(age !== undefined && { age }),
};
users.push(newUser);
// 201 Created を返す
reply.code(201).send({
success: true,
data: newUser,
});
}
このハンドラーでは、リクエストボディがスキーマで検証済みなので、安全にデータにアクセスできます。
ルートを登録する関数を作成します。
typescript// ルート登録
export async function userRoutes(fastify: FastifyInstance) {
// POST /users - ユーザー作成
fastify.post('/users', {
schema: {
...createUserSchema,
...createUserResponseSchema,
tags: ['users'],
summary: 'ユーザーを作成',
description: '新しいユーザーを作成します',
},
handler: createUserHandler,
});
}
スキーマをルート定義に含めることで、自動的にバリデーションとドキュメント生成が行われます。
ユーザー取得エンドポイント
ユーザー一覧取得のスキーマを定義します。
typescript// src/schemas/user.schema.ts に追加
export const getUsersResponseSchema = {
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'array',
items: userSchema,
},
total: { type: 'integer' },
},
},
},
} as const;
ユーザー取得ハンドラーを実装します。
typescript// src/routes/users.routes.ts に追加
async function getUsersHandler(
request: FastifyRequest,
reply: FastifyReply
) {
reply.send({
success: true,
data: users,
total: users.length,
});
}
ルートに追加します。
typescript// userRoutes 関数内に追加
// GET /users - ユーザー一覧取得
fastify.get('/users', {
schema: {
...getUsersResponseSchema,
tags: ['users'],
summary: 'ユーザー一覧を取得',
},
handler: getUsersHandler,
});
これで、ユーザーの作成と取得ができるようになりました。
プラグイン設計
Fastify の強力なプラグインシステムを活用してみましょう。
データベースプラグイン
データベース接続をプラグイン化します。
typescript// src/plugins/database.plugin.ts
import {
FastifyInstance,
FastifyPluginAsync,
} from 'fastify';
import fp from 'fastify-plugin';
// データベース接続の型定義
export interface DatabaseConnection {
query: (sql: string, params?: any[]) => Promise<any>;
close: () => Promise<void>;
}
fastify-plugin
を使うことで、プラグインのスコープを制御できます。
bashyarn add fastify-plugin
プラグインを実装します。
typescript// データベースプラグイン
const databasePlugin: FastifyPluginAsync = async (
fastify: FastifyInstance
) => {
// データベース接続を初期化(ここでは簡易的にモック)
const db: DatabaseConnection = {
query: async (sql: string, params?: any[]) => {
fastify.log.info({ sql, params }, 'Executing query');
return [];
},
close: async () => {
fastify.log.info('Closing database connection');
},
};
// Fastify インスタンスに db を追加
fastify.decorate('db', db);
// アプリ終了時にクリーンアップ
fastify.addHook('onClose', async () => {
await db.close();
});
};
プラグインをエクスポートします。
typescript// 型定義を拡張
declare module 'fastify' {
interface FastifyInstance {
db: DatabaseConnection;
}
}
export default fp(databasePlugin, {
name: 'database',
});
この実装により、すべてのルートで fastify.db
を通じてデータベースにアクセスできるようになります。
認証プラグイン
JWT 認証をプラグイン化してみます。
bashyarn add @fastify/jwt
認証プラグインを作成します。
typescript// src/plugins/auth.plugin.ts
import {
FastifyInstance,
FastifyPluginAsync,
} from 'fastify';
import fp from 'fastify-plugin';
import jwt from '@fastify/jwt';
const authPlugin: FastifyPluginAsync = async (
fastify: FastifyInstance
) => {
// JWT プラグインを登録
await fastify.register(jwt, {
secret: process.env.JWT_SECRET || 'supersecret',
});
// 認証デコレーターを追加
fastify.decorate(
'authenticate',
async function (request, reply) {
try {
await request.jwtVerify();
} catch (err) {
reply.code(401).send({ error: 'Unauthorized' });
}
}
);
};
型定義を拡張します。
typescriptdeclare module 'fastify' {
interface FastifyInstance {
authenticate: (
request: any,
reply: any
) => Promise<void>;
}
}
export default fp(authPlugin, {
name: 'auth',
dependencies: ['jwt'],
});
dependencies
により、JWT プラグインが先に読み込まれることが保証されます。
プラグインの登録
メインサーバーファイルでプラグインを登録しましょう。
typescript// src/server.ts を更新
import Fastify from 'fastify';
import databasePlugin from './plugins/database.plugin';
import authPlugin from './plugins/auth.plugin';
import { userRoutes } from './routes/users.routes';
const fastify = Fastify({
logger: true,
});
プラグインを登録します。
typescript// プラグインを登録
async function registerPlugins() {
await fastify.register(databasePlugin);
await fastify.register(authPlugin);
}
ルートを登録します。
typescript// ルートを登録
async function registerRoutes() {
await fastify.register(userRoutes, { prefix: '/api/v1' });
}
起動処理を更新します。
typescriptconst start = async () => {
try {
await registerPlugins();
await registerRoutes();
await fastify.listen({ port: 3000, host: '0.0.0.0' });
console.log(
'Server is running on http://localhost:3000'
);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();
これで、プラグインとルートが適切な順序で登録され、モジュール化された API が完成しました。
保護されたルートの実装
認証が必要なエンドポイントを作成してみます。
typescript// src/routes/users.routes.ts に追加
// ユーザー削除ハンドラー(認証必須)
async function deleteUserHandler(
request: FastifyRequest<{ Params: { id: string } }>,
reply: FastifyReply
) {
const { id } = request.params;
const index = users.findIndex((u) => u.id === id);
if (index === -1) {
return reply.code(404).send({
success: false,
error: 'User not found',
});
}
users.splice(index, 1);
reply.send({
success: true,
message: 'User deleted successfully',
});
}
認証プリフックを使ってルートを保護します。
typescript// userRoutes 関数内に追加
// DELETE /users/:id - ユーザー削除(認証必須)
fastify.delete('/users/:id', {
preHandler: [fastify.authenticate], // 認証ミドルウェア
schema: {
params: {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
},
required: ['id'],
},
tags: ['users'],
summary: 'ユーザーを削除',
security: [{ bearerAuth: [] }],
},
handler: deleteUserHandler,
});
preHandler
で認証チェックを行い、認証済みユーザーのみがアクセスできるようになります。
エラーハンドリング
統一的なエラーハンドリングを実装しましょう。
typescript// src/plugins/error-handler.plugin.ts
import {
FastifyInstance,
FastifyPluginAsync,
FastifyError,
} from 'fastify';
import fp from 'fastify-plugin';
const errorHandlerPlugin: FastifyPluginAsync = async (
fastify: FastifyInstance
) => {
// カスタムエラーハンドラーを設定
fastify.setErrorHandler(
(error: FastifyError, request, reply) => {
const statusCode = error.statusCode || 500;
fastify.log.error({
error: error.message,
stack: error.stack,
url: request.url,
method: request.method,
});
// 開発環境ではスタックトレースを含める
const response = {
success: false,
error: error.message,
code: error.code,
...(process.env.NODE_ENV === 'development' && {
stack: error.stack,
}),
};
reply.code(statusCode).send(response);
}
);
};
バリデーションエラーも適切に処理します。
typescript // バリデーションエラーのカスタマイズ
fastify.setValidatorCompiler(({ schema }) => {
return (data) => {
// AJV による検証
// エラー時は詳細なメッセージを返す
return { value: data };
};
});
};
export default fp(errorHandlerPlugin, {
name: 'error-handler',
});
このプラグインにより、すべてのエラーが統一的に処理され、適切なログとレスポンスが生成されます。
API ドキュメント生成
Swagger UI でドキュメントを自動生成しましょう。
bashyarn add @fastify/swagger @fastify/swagger-ui
Swagger プラグインを設定します。
typescript// src/plugins/swagger.plugin.ts
import { FastifyInstance, FastifyPluginAsync } from 'fastify';
import fp from 'fastify-plugin';
import swagger from '@fastify/swagger';
import swaggerUi from '@fastify/swagger-ui';
const swaggerPlugin: FastifyPluginAsync = async (fastify: FastifyInstance) => {
// Swagger を登録
await fastify.register(swagger, {
openapi: {
info: {
title: 'Fastify REST API',
description: 'スキーマ駆動の REST API サンプル',
version: '1.0.0',
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server',
},
],
tags: [
{ name: 'users', description: 'ユーザー管理エンドポイント' },
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
},
});
Swagger UI を設定します。
typescript // Swagger UI を登録
await fastify.register(swaggerUi, {
routePrefix: '/docs',
uiConfig: {
docExpansion: 'list',
deepLinking: false,
},
staticCSP: true,
});
};
export default fp(swaggerPlugin, {
name: 'swagger',
});
サーバーに登録します。
typescript// src/server.ts の registerPlugins に追加
import swaggerPlugin from './plugins/swagger.plugin';
async function registerPlugins() {
await fastify.register(swaggerPlugin); // 追加
await fastify.register(databasePlugin);
await fastify.register(authPlugin);
}
これで、http://localhost:3000/docs
にアクセスすると、自動生成された API ドキュメントが表示されます。スキーマから生成されるため、常に最新の状態が保たれますね。
テストの実装
Fastify アプリケーションのテストを書いてみましょう。
bashyarn add -D jest @types/jest ts-jest
yarn add -D @faker-js/faker
Jest の設定ファイルを作成します。
json{
"preset": "ts-jest",
"testEnvironment": "node",
"roots": ["<rootDir>/src"],
"testMatch": ["**/__tests__/**/*.test.ts"],
"collectCoverageFrom": ["src/**/*.ts", "!src/**/*.d.ts"]
}
ユーザー API のテストを実装します。
typescript// src/__tests__/users.test.ts
import Fastify, { FastifyInstance } from 'fastify';
import { userRoutes } from '../routes/users.routes';
describe('User API', () => {
let fastify: FastifyInstance;
// テスト前にサーバーを起動
beforeAll(async () => {
fastify = Fastify();
await fastify.register(userRoutes, { prefix: '/api/v1' });
await fastify.ready();
});
// テスト後にサーバーを終了
afterAll(async () => {
await fastify.close();
});
ユーザー作成のテストを記述します。
typescripttest('POST /api/v1/users - ユーザーを作成できる', async () => {
const response = await fastify.inject({
method: 'POST',
url: '/api/v1/users',
payload: {
name: 'Test User',
email: 'test@example.com',
age: 25,
},
});
expect(response.statusCode).toBe(201);
const body = JSON.parse(response.body);
expect(body.success).toBe(true);
expect(body.data).toHaveProperty('id');
expect(body.data.name).toBe('Test User');
});
バリデーションエラーのテストも追加します。
typescript test('POST /api/v1/users - 不正なデータでエラーになる', async () => {
const response = await fastify.inject({
method: 'POST',
url: '/api/v1/users',
payload: {
name: '', // 空文字はNG
email: 'invalid-email', // 不正なメール形式
},
});
expect(response.statusCode).toBe(400);
});
});
fastify.inject()
を使うことで、実際の HTTP リクエストをシミュレートできます。テストが高速で、ポート待ち受けも不要なので効率的です。
パフォーマンス測定
Fastify のパフォーマンスを測定してみましょう。
typescript// src/routes/benchmark.routes.ts
import { FastifyInstance } from 'fastify';
export async function benchmarkRoutes(
fastify: FastifyInstance
) {
// シンプルな JSON レスポンス
fastify.get('/benchmark/simple', async () => {
return { message: 'Hello, World!' };
});
// スキーマ付き JSON レスポンス
fastify.get(
'/benchmark/schema',
{
schema: {
response: {
200: {
type: 'object',
properties: {
message: { type: 'string' },
timestamp: { type: 'string' },
count: { type: 'integer' },
},
},
},
},
},
async () => {
return {
message: 'Hello, World!',
timestamp: new Date().toISOString(),
count: 1000,
};
}
);
}
スキーマを使ったレスポンスは、fast-json-stringify
により約 2〜3 倍高速化されます。
負荷テストツールで測定してみましょう。
bash# autocannon をインストール
yarn global add autocannon
# ベンチマーク実行
autocannon -c 100 -d 10 http://localhost:3000/benchmark/schema
このコマンドで、100 並列接続、10 秒間の負荷テストを実行できます。結果として、Fastify は高いスループットを示すはずです。
まとめ
本記事では、Fastify を使った REST API 開発について、スキーマ駆動とプラグイン設計を中心に解説しました。
スキーマ駆動開発のポイント
- JSON Schema でリクエスト・レスポンスを定義
- 自動バリデーションとシリアライゼーション
- TypeScript 型との完全な同期
- API ドキュメントの自動生成
プラグインアーキテクチャの利点
- カプセル化された依存関係
- 再利用可能なモジュール
- 明確なライフサイクル管理
- テストしやすい構造
Fastify は、Express の使いやすさを保ちながら、モダンな機能と高速性を提供しています。特に大規模プロジェクトや、パフォーマンスが重要なアプリケーションで真価を発揮するでしょう。
スキーマ駆動開発により、型安全性と開発効率が向上し、プラグインシステムにより保守性の高いコードを書けます。ぜひ、次のプロジェクトで Fastify を試してみてください。
関連リンク
- article
Node.js × Fastify で爆速 REST API:スキーマ駆動とプラグイン設計を学ぶ
- article
Node.js クリーンアーキテクチャ実践:アダプタ/ユースケース/エンティティの分離
- article
htmx × Express/Node.js 高速セットアップ:テンプレ・部分テンプレ構成の定石
- article
Node.js ストリーム逆引き:`pipeline`/`finished`/`stream/promises` 一枚まとめ
- article
Node.js × Corepack 時代のパッケージマネージャ運用:npm/pnpm/yarn を賢く切替
- article
Node.js の fetch 時代を理解する:undici 標準化で何が変わったのか
- article
GPT-5 失敗しないエージェント設計:プランニング/自己検証/停止規則のアーキテクチャ
- article
Remix でスケーラブルなディレクトリ設計:routes/リソース/ユーティリティ分割
- article
Preact でスケーラブルな状態管理:Signals/Context/外部ストアの責務分離
- article
Emotion チートシート:css/styled/Global/Theme の即戦力スニペット 20
- article
Playwright テストデータ設計のベストプラクティス:分離・再現・クリーニング戦略
- article
NotebookLM の始め方:アカウント準備から最初のノート作成まで完全ガイド
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来