netlify について
Netlifyスキルは、JAMstackサイトのデプロイと設定を行う開発者向けのリファレンスです。静的ホスティング、サーバーレス関数、フォーム、ID管理に加え、デプロイプレビューやnetlify.tomlの設定も扱います。
主な機能
- 01静的サイトのデプロイとGit連携
- 02サーバーレス関数とエッジ関数
- 03組み込みフォームとID管理
- 04デプロイプレビューとロールバック
- 05netlify.tomlでのビルド・リダイレクト設定
- 06環境変数の管理
活用シーン
- 01静的サイトやSPAのデプロイ
- 02JAMstackアプリへのバックエンド追加
- 03プルリクエストのデプロイプレビュー設定
- 04サイトの変数とリダイレクトの管理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bobmatnyc/ claude-mpm-skills --skill netlifyインストール後、Claude Code で/netlifyを入力して読み込まれたか確認します。
同梱ファイルmetadata.json
詳細
Netlify プラットフォームスキル
progressive_disclosure: entry_point: summary: "サーバーレス関数、フォーム、ID 管理機能を備えた JAMstack デプロイプラットフォーム" when_to_use: - "静的サイトや SPA をデプロイする場合" - "JAMstack アプリケーションを構築する場合" - "サーバーレス関数が必要な場合" - "組み込みのフォームや認証が必要な場合" quick_start: - "npm install -g netlify-cli" - "netlify login" - "netlify init" - "netlify deploy --prod" token_estimate: entry: 70-85 full: 3800-4800
Netlify の基礎
コアコンセプト
- サイト:Netlify のグローバル CDN にデプロイされる静的サイト
- ビルド:Git のコミットを契機に実行される自動ビルドプロセス
- デプロイコンテキスト:production、deploy-preview、branch-deploy
- アトミックデプロイ:即時ロールバックが可能な、すべて成功するかすべて失敗するデプロイ
- 即時キャッシュ無効化:CDN キャッシュが自動的にクリアされます
プラットフォームのメリット
- グローバル CDN:コンテンツ配信ネットワークを内蔵
- 継続的デプロイ:Git から自動デプロイ
- デフォルトで HTTPS:無料の SSL 証明書
- デプロイプレビュー:すべてのプルリクエストをプレビュー
- サーバーレス関数:サーバーを使わずにバックエンドロジックを実装
- フォームと ID 管理:一般的なニーズに対応する組み込み機能
静的サイトのデプロイ
対応フレームワーク
# React (Create React App, Vite)
Build command: npm run build
Publish directory: build (CRA) or dist (Vite)
# Next.js (Static Export)
Build command: npm run build && npm run export
Publish directory: out
# Vue.js
Build command: npm run build
Publish directory: dist
# Gatsby
Build command: gatsby build
Publish directory: public
# Hugo
Build command: hugo
Publish directory: public
# Svelte/SvelteKit
Build command: npm run build
Publish directory: build
手動デプロイ
# Install Netlify CLI
npm install -g netlify-cli
# Login to Netlify
netlify login
# Initialize site
netlify init
# Deploy draft (preview URL)
netlify deploy
# Deploy to production
netlify deploy --prod
# Deploy with build
netlify deploy --build --prod
netlify.toml の設定
基本設定
# netlify.toml
[build]
# Build command
command = "npm run build"
# Publish directory
publish = "dist"
# Functions directory
functions = "netlify/functions"
# Production context
[context.production]
command = "npm run build:prod"
[context.production.environment]
NODE_ENV = "production"
API_URL = "https://api.example.com"
# Deploy Preview context
[context.deploy-preview]
command = "npm run build:preview"
# Branch deploys
[context.branch-deploy]
command = "npm run build"
ビルド設定
[build]
command = "npm run build"
publish = "dist"
# Base directory for monorepos
base = "packages/web"
# Ignore builds on certain changes
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF packages/web"
[build.environment]
NODE_VERSION = "18"
NPM_VERSION = "9"
RUBY_VERSION = "3.1"
高度なビルド設定
[build]
command = "npm run build"
publish = "dist"
# Build processing
[build.processing]
skip_processing = false
[build.processing.css]
bundle = true
minify = true
[build.processing.js]
bundle = true
minify = true
[build.processing.images]
compress = true
環境変数
変数の設定
# Via CLI
netlify env:set API_KEY "secret-value"
netlify env:set NODE_ENV "production"
# List variables
netlify env:list
# Import from .env file
netlify env:import .env
変数のスコープ
# netlify.toml
[context.production.environment]
API_URL = "https://api.production.com"
ENABLE_ANALYTICS = "true"
[context.deploy-preview.environment]
API_URL = "https://api.staging.com"
ENABLE_ANALYTICS = "false"
[context.branch-deploy.environment]
API_URL = "https://api.dev.com"
ビルドでのアクセス
// During build
const apiUrl = process.env.API_URL;
// Client-side (must be prefixed)
const publicKey = process.env.REACT_APP_PUBLIC_KEY;
サーバーレス関数
関数の構成
// netlify/functions/hello.js
exports.handler = async (event, context) => {
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'Hello from Netlify Function',
path: event.path,
method: event.httpMethod,
}),
};
};
TypeScript 関数
// netlify/functions/api.ts
import { Handler, HandlerEvent, HandlerContext } from '@netlify/functions';
interface RequestBody {
name: string;
email: string;
}
export const handler: Handler = async (
event: HandlerEvent,
context: HandlerContext
) => {
if (event.httpMethod !== 'POST') {
return {
statusCode: 405,
body: 'Method Not Allowed',
};
}
const { name, email }: RequestBody = JSON.parse(event.body || '{}');
return {
statusCode: 200,
body: JSON.stringify({
message: `Hello ${name}`,
email,
}),
};
};
高度な関数パターン
// netlify/functions/database.js
const { MongoClient } = require('mongodb');
let cachedDb = null;
async function connectToDatabase() {
if (cachedDb) return cachedDb;
const client = await MongoClient.connect(process.env.MONGODB_URI);
cachedDb = client.db();
return cachedDb;
}
exports.handler = async (event) => {
const db = await connectToDatabase();
const users = await db.collection('users').find({}).toArray();
return {
statusCode: 200,
body: JSON.stringify(users),
};
};
スケジュール関数
// netlify/functions/scheduled.js
const { schedule } = require('@netlify/functions');
const handler = async () => {
console.log('Running scheduled task');
// Your scheduled logic
await performBackup();
return {
statusCode: 200,
};
};
// Run every day at midnight
exports.handler = schedule('0 0 * * *', handler);
バックグラウンド関数
// netlify/functions/background-task.js
// Auto-runs in background if response is 200 within 10s
exports.handler = async (event) => {
// Long-running task
await processLargeDataset();
return {
statusCode: 200,
};
};
// Invoke: POST to /.netlify/functions/background-task
Netlify Forms
HTML フォーム
<form name="contact" method="POST" data-netlify="true">
<input type="hidden" name="form-name" value="contact" />
<label>Name: <input type="text" name="name" required /></label>
<label>Email: <input type="email" name="email" required /></label>
<label>Message: <textarea name="message" required></textarea></label>
<button type="submit">Send</button>
</form>
React フォーム
// ContactForm.jsx
import { useState } from 'react';
export default function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: '',
});
const handleSubmit = async (e) => {
e.preventDefault();
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
'form-name': 'contact',
...formData,
}).toString(),
});
if (response.ok) {
alert('Thank you for your message!');
}
};
return (
<form name="contact" onSubmit={handleSubmit} data-netlify="true">
<input type="hidden" name="form-name" value="contact" />
{/* Form fields */}
</form>
);
}
フォーム機能
<form name="contact" method="POST" data-netlify="true" data-netlify-honeypot="bot-field">
<input type="hidden" name="form-name" value="contact" />
<p class="hidden">
<label>Don't fill this out: <input name="bot-field" /></label>
</p>
</form>
<form name="contact" method="POST" data-netlify="true" data-netlify-recaptcha="true">
<div data-netlify-recaptcha="true"></div>
<button type="submit">Submit</button>
</form>
<form name="file-upload" method="POST" data-netlify="true" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">Upload</button>
</form>
フォーム通知
# netlify.toml
[[plugins]]
package = "@netlify/plugin-emails"
[plugins.inputs]
formName = "contact"
to = "[email protected]"
subject = "New contact form submission"
Netlify Identity
Identity の有効化
// Add to HTML
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
// Initialize
if (window.netlifyIdentity) {
window.netlifyIdentity.on('init', user => {
if (!user) {
window.netlifyIdentity.on('login', () => {
document.location.href = '/admin/';
});
}
});
}
React との統合
// useNetlifyIdentity.js
import { useEffect, useState } from 'react';
export function useNetlifyIdentity() {
const [user, setUser] = useState(null);
useEffect(() => {
const netlifyIdentity = window.netlifyIdentity;
netlifyIdentity.on('init', user => setUser(user));
netlifyIdentity.on('login', user => setUser(user));
netlifyIdentity.on('logout', () => setUser(null));
netlifyIdentity.init();
}, []);
return {
user,
login: () => window.netlifyIdentity.open('login'),
logout: () => window.netlifyIdentity.logout(),
signup: () => window.netlifyIdentity.open('signup'),
};
}
保護された関数
// netlify/functions/protected.js
exports.handler = async (event, context) => {
const { user } = context.clientContext;
if (!user) {
return {
statusCode: 401,
body: 'Unauthorized',
};
}
return {
statusCode: 200,
body: JSON.stringify({
message: 'Protected data',
user: user.email,
}),
};
};
リダイレクトとリライト
_redirects ファイル
# _redirects file in publish directory
# Redirect with status code
/old-path /new-path 301
# Rewrite (proxy)
/api/* https://api.example.com/:splat 200
# SPA fallback
/* /index.html 200
# Force HTTPS
http://example.com/* https://example.com/:splat 301!
# Conditional redirects
/news/* /blog/:splat 302 Country=us
# Role-based redirects
/admin/* /admin/dashboard 200! Role=admin
/admin/* /unauthorized 401
netlify.toml のリダイレクト
[[redirects]]
from = "/old-path"
to = "/new-path"
status = 301
[[redirects]]
from = "/api/*"
to = "https://api.example.com/:splat"
status = 200
force = true
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[redirects]]
from = "/admin/*"
to = "/admin/dashboard"
status = 200
conditions = {Role = ["admin"]}
# Proxy with headers
[[redirects]]
from = "/proxy/*"
to = "https://backend.com/:splat"
status = 200
force = true
[redirects.headers]
X-From = "Netlify"
カスタムヘッダー
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
Content-Security-Policy = "default-src 'self'"
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
デプロイプレビュー
自動プレビュー URL
# netlify.toml
[context.deploy-preview]
command = "npm run build:preview"
[context.deploy-preview.environment]
NODE_ENV = "preview"
API_URL = "https://api-staging.example.com"
ブランチデプロイ
# Deploy specific branches
[context.staging]
command = "npm run build:staging"
[context.staging.environment]
API_URL = "https://api-staging.example.com"
# Branch pattern matching
[context.branch-deploy]
command = "npm run build"
デプロイ通知
# GitHub PR comments
# Slack notifications
# Email notifications
# Configured in Netlify UI
スプリットテスト(A/B テスト)
設定
# netlify.toml
[[redirects]]
from = "/*"
to = "/version-a/:splat"
status = 200
conditions = {Cookie = ["ab_test=a"]}
force = true
[[redirects]]
from = "/*"
to = "/version-b/:splat"
status = 200
conditions = {Cookie = ["ab_test=b"]}
force = true
# 50/50 split
[[redirects]]
from = "/*"
to = "/version-a/:splat"
status = 200!
percentage = 50
[[redirects]]
from = "/*"
to = "/version-b/:splat"
status = 200!
Edge Functions
Deno ランタイム
// netlify/edge-functions/hello.ts
import type { Context } from "https://edge.netlify.com";
export default async (request: Request, context: Context) => {
const url = new URL(request.url);
return new Response(`Hello from ${url.pathname}`, {
headers: { "content-type": "text/html" },
});
};
export const config = { path: "/hello" };
ジオロケーション
// netlify/edge-functions/geo.ts
import type { Context } from "https://edge.netlify.com";
export default async (request: Request, context: Context) => {
const { city, country } = context.geo;
return Response.json({
location: `${city}, ${country}`,
});
};
レスポンスの変換
// netlify/edge-functions/transform.ts
import type { Context } from "https://edge.netlify.com";
export default async (request: Request, context: Context) => {
const response = await context.next();
const text = await response.text();
// Modify HTML
const modified = text.replace(
'</body>',
'<script>console.log("Injected by edge");</script></body>'
);
return new Response(modified, response);
};
export const config = { path: "/*" };
カスタムドメインと SSL
カスタムドメインの追加
# Via CLI
netlify domains:add example.com
# DNS Configuration
# A record: 75.2.60.5
# CNAME: <site-name>.netlify.app
# Verify domain
netlify domains:verify example.com
SSL 証明書
# Automatic HTTPS (default)
# Free Let's Encrypt certificates
# Auto-renewal
# Force HTTPS redirect
[[redirects]]
from = "http://example.com/*"
to = "https://example.com/:splat"
status = 301
force = true
アナリティクス
Netlify Analytics
カスタムアナリティクス
// Track custom events
function trackEvent(eventName, data) {
fetch('/.netlify/functions/analytics', {
method: 'POST',
body: JSON.stringify({ event: eventName, ...data }),
});
}
trackEvent('button_click', { button: 'signup' });
CLI の高度な使い方
開発サーバー
# Run functions locally
netlify dev
# Specific port
netlify dev --port 3000
# Live session sharing
netlify dev --live
# Functions only
netlify functions:serve
サイト管理
# Link existing site
netlify link
# Create new site
netlify sites:create
# List sites
netlify sites:list
# Site info
netlify status
# Open site in browser
netlify open
デプロイ管理
# List deploys
netlify deploy:list
# Rollback to previous deploy
netlify rollback
# Cancel deploy
netlify deploy:cancel <deploy-id>
Git 連携
継続的デプロイ
# netlify.toml
[build]
command = "npm run build"
publish = "dist"
# Auto-publish on Git push
# Production: main/master branch
# Previews: all pull requests
# Branch deploys: configured branches
デプロイフック
# Trigger builds via webhook
curl -X POST -d {} https://api.netlify.com/build_hooks/<hook-id>
# Scheduled builds (use external cron + webhook)
ベストプラクティス
パフォーマンス最適化
# Enable processing
[build.processing]
skip_processing = false
[build.processing.css]
bundle = true
minify = true
[build.processing.js]
bundle = true
minify = true
[build.processing.images]
compress = true
# Asset optimization
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
セキュリティヘッダー
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "geolocation=(), microphone=(), camera=()"
Content-Security-Policy = """
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;
"""
関数のベストプラクティス
// Keep functions lightweight
// Use connection pooling
// Cache external API responses
// Set appropriate timeouts
// Handle errors gracefully
exports.handler = async (event) => {
try {
// Set timeout
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
const response = await fetch(API_URL, {
signal: controller.signal,
});
clearTimeout(timeout);
return {
statusCode: 200,
body: JSON.stringify(await response.json()),
};
} catch (error) {
console.error('Function error:', error);
return {
statusCode: 500,
body: JSON.stringify({ error: 'Internal server error' }),
};
}
};
ビルドの最適化
[build]
command = "npm run build"
publish = "dist"
# Skip builds when not needed
ignore = """
git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF -- . ':(exclude)docs/' ':(exclude)*.md'
"""
# Cache dependencies
[build.environment]
NPM_FLAGS = "--legacy-peer-deps"
NODE_OPTIONS = "--max-old-space-size=4096"
モノレポのサポート
# netlify.toml
[build]
base = "packages/web"
command = "npm run build"
publish = "dist"
# Only build when package changes
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF packages/web"
よくあるパターン
API プロキシを備えた SPA
[build]
command = "npm run build"
publish = "build"
functions = "netlify/functions"
[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
サブフォルダーを使ったマイクロサイト
[[redirects]]
from = "/blog/*"
to = "https://blog.example.com/:splat"
status = 200
force = true
認証ゲートウェイ
[[redirects]]
from = "/app/*"
to = "/app/dashboard"
status = 200
conditions = {Role = ["user"]}
[[redirects]]
from = "/app/*"
to = "/login"
status = 302
概要:Netlify は、静的ホスティング、サーバーレス関数、フォーム、ID 管理機能を備えた包括的な JAMstack プラットフォームです。主な強みは、アトミックデプロイ、即時キャッシュ無効化、デプロイプレビュー、組み込み CDN です。ビルド、リダイレクト、ヘッダー、環境ごとの設定には netlify.toml を使用します。バックエンドロジックにはサーバーレス関数、ユーザー入力にはフォーム、エッジコンピューティングには Edge Functions を活用します。ベストプラクティスには、パフォーマンス最適化、セキュリティヘッダー、効率的なビルド設定が含まれます。
コメント
Markdown 対応