<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>開発事例 ‣ てんハロ｜未経験エンジニアのIT学習ログ</title>
	<atom:link href="https://it-bokenki.com/category/projects/feed/" rel="self" type="application/rss+xml" />
	<link>https://it-bokenki.com</link>
	<description>Hello Worldから、今日も生きてる</description>
	<lastBuildDate>Wed, 23 Jul 2025 06:35:28 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://it-bokenki.com/wp-content/uploads/2025/06/cropped-ブログ　アイコン-32x32.png</url>
	<title>開発事例 ‣ てんハロ｜未経験エンジニアのIT学習ログ</title>
	<link>https://it-bokenki.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>【初心者OK】FastAPI×MySQLをDockerで連携！Pythonで爆速開発する方法</title>
		<link>https://it-bokenki.com/2025/07/10/fastapi/</link>
					<comments>https://it-bokenki.com/2025/07/10/fastapi/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Wed, 09 Jul 2025 15:32:30 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[バックエンド]]></category>
		<category><![CDATA[開発事例]]></category>
		<category><![CDATA[Docker]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=4594</guid>

					<description><![CDATA[<p>未経験でも気軽に！サブスク型プログラミングスクール【Freeks】 困ってた自分に届けたい話 ・・・いや、はじめてのPython開発でそれ言う！？フォルダ構成？DB接続？何もわからん！ でも、調べまくって、なんとか Fa [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/07/10/fastapi/">【初心者OK】FastAPI×MySQLをDockerで連携！Pythonで爆速開発する方法</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=457GS5+A4DB02+5JVK+BXQOH">
<img fetchpriority="high" decoding="async" border="0" width="300" height="250" alt="" src="https://www20.a8.net/svt/bgt?aid=250611125612&#038;wid=001&#038;eno=01&#038;mid=s00000025904002005000&#038;mc=1"></a>
<img decoding="async" border="0" width="1" height="1" src="https://www12.a8.net/0.gif?a8mat=457GS5+A4DB02+5JVK+BXQOH" alt=""></div>



<p class="has-text-align-center"><a href="https://px.a8.net/svt/ejp?a8mat=457GS5+A4DB02+5JVK+BX3J6">未経験でも気軽に！サブスク型プログラミングスクール【Freeks】</a></p>



<h2 class="wp-block-heading"><span id="toc1">困ってた自分に届けたい話</span></h2>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/themes/cocoon-master/images/man.png" alt="上司" class="speech-icon-image"/></figure><div class="speech-name">上司</div></div><div class="speech-balloon">
<p>FastAPIで、フロントから送られた<br>名前・メールアドレス・パスワードをMySQLに登録しておいて</p>
</div></div>



<p>・・・いや、<strong>はじめてのPython開発でそれ言う！？</strong><br>フォルダ構成？DB接続？何もわからん！</p>



<p>でも、調べまくって、なんとか <strong>FastAPI × MySQL × Docker</strong> で爆速開発できました。</p>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-r sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>この記事は、同じように困っていた方への<strong>備忘録兼シェア</strong>として書いています。</p>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">使用した技術スタック</span></h2>



<ul class="wp-block-list">
<li><strong>FastAPI</strong>（Python製の軽量Webフレームワーク）</li>



<li><strong>MySQL</strong>（データベース）</li>



<li><strong>Docker Compose</strong>（複数のコンテナをまとめて起動）</li>



<li><strong>Next.js</strong>（フロントエンド／フォーム送信で使用）</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc3">プロジェクト構成</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" width="940" height="640" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.18.48.png" alt="【初心者OK】FastAPI×MySQLをDockerで連携！Python開発環境を最速構築する方法" class="wp-image-4624" style="width:760px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.18.48.png 940w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.18.48-300x204.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.18.48-768x523.png 768w" sizes="(max-width: 940px) 100vw, 940px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc4">FastAPI × Docker 開発環境のセットアップ</span></h2>



<h3 class="wp-block-heading"><span id="toc5">環境変数（.env.local）の設定方法</span></h3>



<p>環境変数をルート直下に <code>.env.local</code> として作成します。（開発環境で実行するため）<br>Dockerコンテナ間通信では <code>DB_HOST</code> を MySQL サービス名にします。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>DB_HOST=
DB_PORT=
DB_NAME=
DB_USER=
DB_PASSWORD=</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">DB_HOST</span><span style="color: #81A1C1">=</span></span>
<span class="line"><span style="color: #D8DEE9">DB_PORT</span><span style="color: #81A1C1">=</span></span>
<span class="line"><span style="color: #D8DEE9">DB_NAME</span><span style="color: #81A1C1">=</span></span>
<span class="line"><span style="color: #D8DEE9">DB_USER</span><span style="color: #81A1C1">=</span></span>
<span class="line"><span style="color: #D8DEE9">DB_PASSWORD</span><span style="color: #81A1C1">=</span></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc6">Docker（MySQL × FastAPI 連携）の書き方</span></h3>



<p><span class="keyboard-key">対象ファイル</span>：docker/docker-compose.yml</p>



<p>MySQLとFastAPIを同時に立ち上げるDocker Compose設定です。</p>



<p>すでにMySQLの設定がある場合は、FastAPIの定義だけ追加してください。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>version: '3'
services:
  mysql:
    image: mysql:8.0
    ... # 詳細は別記事へ

  FastAPI:
    build:
      context: ..　　// Dockerfile がある位置を指定
      dockerfile: docker/Dockerfile
    container_name: 任意のコンテナ名
    ports:
      - "8000:8000"
    volumes:
      - ../backend:/app
    env_file:
      - ../.env.local
    depends_on:　　// 「このアプリは mysql が先に起動していないとダメだよ」という意味
      - mysql</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">version</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">3</span><span style="color: #ECEFF4">&#39;</span></span>
<span class="line"><span style="color: #D8DEE9FF">services</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">  mysql</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">    image</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> mysql</span><span style="color: #ECEFF4">:</span><span style="color: #B48EAD">8.0</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9FF"> # </span><span style="color: #D8DEE9">詳細は別記事へ</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  FastAPI</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">    build</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      context</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">..</span><span style="color: #D8DEE9FF">　　</span><span style="color: #616E88">// Dockerfile がある位置を指定</span></span>
<span class="line"><span style="color: #D8DEE9FF">      dockerfile</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">docker</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">Dockerfile</span></span>
<span class="line"><span style="color: #D8DEE9FF">    container_name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">任意のコンテナ名</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ports</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">8000:8000</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    volumes</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">..</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9FF">backend</span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">app</span></span>
<span class="line"><span style="color: #D8DEE9FF">    env_file</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">..</span><span style="color: #81A1C1">/</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">local</span></span>
<span class="line"><span style="color: #D8DEE9FF">    depends_on</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF">　　</span><span style="color: #616E88">// 「このアプリは mysql が先に起動していないとダメだよ」という意味</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">mysql</span></span></code></pre></div>



<p>MySQLの設定方法は下記の記事をチェックしてください。</p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2025/07/03/docker-mysql/" title="DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-376x212.png 376w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説</div><div class="blogcard-snippet internal-blogcard-snippet">DockerでMacにMySQLの開発環境を一瞬で構築！Apple Silicon対応のインストール手順、docker-compose.ymlと.env連携、utf8mb4設定、ターミナル操作でのDB作成まで初心者にもわかりやすく解説しています。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc7">FastAPIアプリ起動設定</span></h3>



<p><span class="keyboard-key">対象ファイル</span>：docker/ Dockerfile</p>



<p>FastAPIアプリの<strong>起動手順を定義</strong>する Dockerfile を用意します。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x26a0;&#xfe0f; 注意点</span></p>



<ul class="wp-block-list">
<li>大文字スタートの <code>Dockerfile</code> というファイル名でなければいけない</li>



<li>拡張子は <strong>不要・つけてはいけない</strong>（<code>.txt</code>や<code>.py</code>などはNG）</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// Pythonの軽くて早いバージョンを指定
FROM python:3.10-slim

// 実行時に.pycファイルを作らないように設定（大量にできてGitに上げれなくなる）
ENV PYTHONDONTWRITEBYTECODE=1

// コンテナ内で作業するディレクトリを作成
WORKDIR /app

// ホスト側の ./backend フォルダを、コンテナ内の /app に丸ごとコピー
COPY ./backend /app

// requirements.txt をコピーして、必要なライブラリをインストール
COPY ./backend/requirements.txt .

RUN pip install --upgrade pip
RUN pip install -r requirements.txt

// アプリを起動
CMD &#91;"uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"&#93;</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// Pythonの軽くて早いバージョンを指定</span></span>
<span class="line"><span style="color: #D8DEE9">FROM</span><span style="color: #D8DEE9FF"> python</span><span style="color: #ECEFF4">:</span><span style="color: #B48EAD">3.10</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">slim</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 実行時に.pycファイルを作らないように設定（大量にできてGitに上げれなくなる）</span></span>
<span class="line"><span style="color: #D8DEE9">ENV</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">PYTHONDONTWRITEBYTECODE</span><span style="color: #81A1C1">=</span><span style="color: #B48EAD">1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// コンテナ内で作業するディレクトリを作成</span></span>
<span class="line"><span style="color: #D8DEE9">WORKDIR</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">app</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// ホスト側の ./backend フォルダを、コンテナ内の /app に丸ごとコピー</span></span>
<span class="line"><span style="color: #D8DEE9">COPY</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">.</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">backend</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">app</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// requirements.txt をコピーして、必要なライブラリをインストール</span></span>
<span class="line"><span style="color: #D8DEE9">COPY</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">.</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">backend</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">requirements</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">txt</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">.</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9">RUN</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pip</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">install</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">--</span><span style="color: #D8DEE9">upgrade</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pip</span></span>
<span class="line"><span style="color: #D8DEE9">RUN</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pip</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">install</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">r</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">requirements</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">txt</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// アプリを起動</span></span>
<span class="line"><span style="color: #D8DEE9">CMD</span><span style="color: #D8DEE9FF"> &#91;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">uvicorn</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">main:app</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">--host</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0.0.0.0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">--port</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">8000</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">--reload</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">&#93;</span></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc8">requirements.txt の作成とバージョン管理</span></h3>



<p><span class="keyboard-key">対象ファイル</span>：backend/requirements.txt</p>



<p>Pythonパッケージの<strong>インストール一覧</strong>を記述します。</p>



<p>下記のコマンドで、Dockerで一括で自動インストールすることができます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>pip install -r requirements.txt </code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x1f331; 豆知識</span></p>



<p><strong>パッケージ名しか書いていない場合</strong>：常に最新バージョンがインストールされる（試作段階・練習中向け）</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>fastapi
uvicorn
SQLAlchemy
PyMySQL
python-dotenv</code></pre></div>



<p><strong>バージョンを指定している場合</strong>：誰が使っても同じ環境になる（チーム開発・本番運用向け）</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>fastapi==0.116.0
uvicorn==0.35.0
SQLAlchemy==2.0.41
PyMySQL==1.1.1
python-dotenv==1.1.1</code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc9">.gitignore の設定でGitをクリーンに保つ</span></h2>



<p><span class="keyboard-key">対象ファイル</span>：.gitignore</p>



<p>Pythonのをインストールすると自動生成ファイルができて、Gitにそのまま上げると大変なことになります。<br>下記のように設定すると、Gitに不要なファイルが混ざらなくなります。おすすめです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code># バーチャル環境除外
venv/

# Pythonのバイトコードキャッシュを無視
__pycache__/
*.pyc
*.pyo
*.pyd</code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc10">テーブル作成</span></h2>



<p>事前に <code>users</code> テーブルを作っておきましょう。DockerのMySQL内で作成できます。</p>



<p>作成方法については、下記の記事をご覧ください。</p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2025/07/03/drizzle/" title="Drizzleとは？TypeScriptで型安全にSQL・マイグレーションを管理する方法" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/07/1-1-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/1-1-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/07/1-1-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/1-1-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/1-1-376x212.png 376w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">Drizzleとは？TypeScriptで型安全にSQL・マイグレーションを管理する方法</div><div class="blogcard-snippet internal-blogcard-snippet">Drizzle ORMを使って、TypeScriptで型安全にSQLやマイグレーションを管理する方法をわかりやすく解説。初期設定からスキーマ定義、リレーション設定まで丁寧に紹介！</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
</div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2025/07/03/docker-mysql/" title="DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-376x212.png 376w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説</div><div class="blogcard-snippet internal-blogcard-snippet">DockerでMacにMySQLの開発環境を一瞬で構築！Apple Silicon対応のインストール手順、docker-compose.ymlと.env連携、utf8mb4設定、ターミナル操作でのDB作成まで初心者にもわかりやすく解説しています。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc11">Next.jsで登録フォームを作成</span></h2>



<p>下記は登録フォームの <code>registerForm.tsx</code> の内容の内容です。<br><code>register/page.tsx</code> で呼び出すと、下記の画像のような登録フォームになります。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>"use client";

import { useState } from "react";

export default function RegisterForm() {
  const &#91;form, setForm&#93; = useState({
    name: "",
    email: "",
    password: "",
  });

  const &#91;message, setMessage&#93; = useState("");

  const handleChange = (e: React.ChangeEvent&lt;HTMLInputElement>) => {
    setForm({ ...form, &#91;e.target.name&#93;: e.target.value });
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      const res = await fetch("http://localhost:8000/registration", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(form),
      });

      const data = await res.json();
      if (res.ok) {
        setMessage("登録成功！ ユーザーID: " + data.user_id);
      } else {
        setMessage("登録失敗: " + (data.detail || "エラーが発生しました"));
      }
    } catch (error) {
      console.error("エラー:", error);
      setMessage("通信エラーが発生しました");
    }
  };

  return (
    &lt;form onSubmit={handleSubmit} className="space-y-4 max-w-md mx-auto mt-10">
      &lt;div>
        &lt;label htmlFor="name" className="block font-medium mb-1">
          お名前
        &lt;/label>
        &lt;input
          id="name"
          name="name"
          placeholder="例：山田 太郎"
          value={form.name}
          onChange={handleChange}
          required
          className="border px-3 py-2 w-full"
        />
      &lt;/div>

      &lt;div>
        &lt;label htmlFor="email" className="block font-medium mb-1">
          メールアドレス
        &lt;/label>
        &lt;input
          id="email"
          name="email"
          placeholder="例：taro@example.com"
          type="email"
          value={form.email}
          onChange={handleChange}
          required
          className="border px-3 py-2 w-full"
        />
      &lt;/div>

      &lt;div>
        &lt;label htmlFor="password" className="block font-medium mb-1">
          パスワード
        &lt;/label>
        &lt;input
          id="password"
          name="password"
          placeholder="半角英数字8文字以上"
          type="password"
          value={form.password}
          onChange={handleChange}
          required
          className="border px-3 py-2 w-full"
        />
      &lt;/div>

      &lt;button
        type="submit"
        className="bg-blue-600 text-white px-4 py-2 rounded w-full"
      >
        ユーザー登録
      &lt;/button>

      {message &amp;&amp; &lt;p className="mt-4 text-sm text-gray-700">{message}&lt;/p>}
    &lt;/form>
  );
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">use client</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">useState</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">react</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">RegisterForm</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#91;</span><span style="color: #D8DEE9">form</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setForm</span><span style="color: #ECEFF4">&#93;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">email</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">password</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#91;</span><span style="color: #D8DEE9">message</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setMessage</span><span style="color: #ECEFF4">&#93;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleChange</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">ChangeEvent</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLInputElement</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setForm</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">...</span><span style="color: #D8DEE9">form</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">&#91;</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">target</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">name</span><span style="color: #88C0D0">&#93;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">target</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">value</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleSubmit</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">FormEvent</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">preventDefault</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetch</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">http://localhost:8000/registration</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">method</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">POST</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">headers</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Content-Type</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">application/json</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">body</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">JSON</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">stringify</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">form</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ok</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">登録成功！ ユーザーID: </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">user_id</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">登録失敗: </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">detail</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">||</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">エラーが発生しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">error</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">エラー:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">通信エラーが発生しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;form</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">onSubmit</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleSubmit</span><span style="color: #81A1C1">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">space-y-4 max-w-md mx-auto mt-10</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;label</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">htmlFor</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">name</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">block font-medium mb-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          お名前</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/label&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;input</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">id</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">name</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">name</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">name</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">placeholder</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">例：山田 太郎</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">value</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">form</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">onChange</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleChange</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">required</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">border px-3 py-2 w-full</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;label</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">htmlFor</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">email</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">block font-medium mb-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          メールアドレス</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/label&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;input</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">id</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">email</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">name</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">email</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">placeholder</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">例：taro@example.com</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">email</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">value</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">form</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">email</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">onChange</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleChange</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">required</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">border px-3 py-2 w-full</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;label</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">htmlFor</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">block font-medium mb-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          パスワード</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/label&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;input</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">id</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">password</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">name</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">password</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">placeholder</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">半角英数字8文字以上</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">password</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">value</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">form</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">password</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">onChange</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleChange</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">required</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">border px-3 py-2 w-full</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">submit</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">bg-blue-600 text-white px-4 py-2 rounded w-full</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        ユーザー登録</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #D8DEE9">message</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;p</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mt-4 text-sm text-gray-700</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;{</span><span style="color: #D8DEE9">message</span><span style="color: #81A1C1">}&lt;/p&gt;}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/form&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="575" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-1024x575.png" alt="【初心者OK】FastAPI×MySQLをDockerで連携！Python開発環境を最速構築する方法" class="wp-image-4625" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-1024x575.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-300x168.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-768x431.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-376x212.png 376w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03-1320x741.png 1320w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-09-18.29.03.png 1340w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc12">FastAPIのバックエンド側の実装</span></h2>



<h3 class="wp-block-heading"><span id="toc13">各ファイルの役割</span></h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>ファイル</th><th>内容</th></tr></thead><tbody><tr><td><code>backend/db.py</code></td><td>DB接続の設定を記述</td></tr><tr><td><code>backend/main.py</code></td><td>FastAPIの本体とCORS、ルーティング設定</td></tr><tr><td><code>backend/models/user.py</code></td><td>usersテーブル定義（UUID、カラムなど）</td></tr><tr><td><code>backend/schemas/user.py</code></td><td>Pydanticで受け取りバリデーション</td></tr><tr><td><code>backend/utils/common_fields.py</code></td><td>登録時の共通フィールドを自動付与</td></tr><tr><td><code>backend/routers/registration.py</code></td><td>POSTでデータ受取→DB保存処理</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc14">db.py：DB接続設定</span></h3>



<p><code>.env.local </code>を読み込んで DB に接続する準備をする</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>from sqlalchemy import create_engine // DBとの接続エンジンを作るための関数
from sqlalchemy.orm import sessionmaker, declarative_base
from dotenv import load_dotenv  // .envファイルから環境変数を読み込むための関数
import os // 環境変数を取得するための標準ライブラリ

// ルートの.env読み込み
load_dotenv(dotenv_path=".env.local")

DB_HOST = os.getenv("DB_HOST")
DB_PORT = os.getenv("DB_PORT")
DB_NAME = os.getenv("DB_NAME")
DB_USER = os.getenv("DB_USER")
DB_PASSWORD = os.getenv("DB_PASSWORD")

// データベース接続用のURLを作成
DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"

// 接続準備
engine = create_engine(DB_URL)
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
Base = declarative_base()</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">sqlalchemy</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">create_engine</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// DBとの接続エンジンを作るための関数</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">sqlalchemy</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">orm</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">sessionmaker</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">declarative_base</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">dotenv</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">load_dotenv</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// .envファイルから環境変数を読み込むための関数</span></span>
<span class="line"><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">os</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 環境変数を取得するための標準ライブラリ</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// ルートの.env読み込み</span></span>
<span class="line"><span style="color: #8FBCBB">load_dotenv</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">dotenv_path</span><span style="color: #D8DEE9FF">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.env.local</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #8FBCBB">DB_HOST</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">os</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">getenv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">DB_HOST</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #8FBCBB">DB_PORT</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">os</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">getenv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">DB_PORT</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #8FBCBB">DB_NAME</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">os</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">getenv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">DB_NAME</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #8FBCBB">DB_USER</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">os</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">getenv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">DB_USER</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #8FBCBB">DB_PASSWORD</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">os</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">getenv</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">DB_PASSWORD</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// データベース接続用のURLを作成</span></span>
<span class="line"><span style="color: #8FBCBB">DB_URL</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">f</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// 接続準備</span></span>
<span class="line"><span style="color: #8FBCBB">engine</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">create_engine</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">DB_URL</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #8FBCBB">SessionLocal</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">sessionmaker</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">bind</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">engine</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">autocommit</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">False</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">autoflush</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">False</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #8FBCBB">Base</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">declarative_base</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc15">main.py：アプリ起動とCORS設定</span></h3>



<p>FastAPIアプリを起動し、CORS設定とルーター登録を行います。</p>



<p>Next.js開発環境で実行しているので、今回は<code>http://localhost:3000</code>を指定（ここは環境変数設定にしたほうがいいかもです）</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware 　// 別ドメインからのアクセスを許可するための設定
from routers import registration

// FastAPIアプリケーションの本体を作成
app = FastAPI()

// CORS（クロスオリジン）設定を追加：別のドメインからアクセスを許可する
app.add_middleware(
    CORSMiddleware,
    allow_origins=&#91;"http://localhost:3000"&#93;,  // フロントエンド（Next.js）からのアクセスを許可
    allow_credentials=True,  // クッキーなどの認証情報を含めた通信を許可
    allow_methods=&#91;"*"&#93;,  // GETやPOSTなど、すべてのHTTPメソッドを許可
    allow_headers=&#91;"*"&#93;,  // すべてのリクエストヘッダーを許可（認証トークンなど）
)

// APIの処理を追加（今回はregistration.pyのルーター）
app.include_router(registration.router)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fastapi</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">FastAPI</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">fastapi</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">middleware</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">cors</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">CORSMiddleware</span><span style="color: #D8DEE9FF"> 　</span><span style="color: #616E88">// 別ドメインからのアクセスを許可するための設定</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">routers</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">registration</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// FastAPIアプリケーションの本体を作成</span></span>
<span class="line"><span style="color: #8FBCBB">app</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">FastAPI</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// CORS（クロスオリジン）設定を追加：別のドメインからアクセスを許可する</span></span>
<span class="line"><span style="color: #8FBCBB">app</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">add_middleware</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">CORSMiddleware</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">allow_origins</span><span style="color: #D8DEE9FF">=&#91;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">http://localhost:3000</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// フロントエンド（Next.js）からのアクセスを許可</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">allow_credentials</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">True</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// クッキーなどの認証情報を含めた通信を許可</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">allow_methods</span><span style="color: #D8DEE9FF">=&#91;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">*</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// GETやPOSTなど、すべてのHTTPメソッドを許可</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">allow_headers</span><span style="color: #D8DEE9FF">=&#91;</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">*</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">&#93;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// すべてのリクエストヘッダーを許可（認証トークンなど）</span></span>
<span class="line"><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// APIの処理を追加（今回はregistration.pyのルーター）</span></span>
<span class="line"><span style="color: #8FBCBB">app</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">include_router</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">registration</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">router</span><span style="color: #D8DEE9FF">)</span></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc16">models/user.py：ユーザーテーブル定義</span></h3>



<p>SQLAlchemyで <code>userテーブル</code> 構造を定義します。主キーは <code>UUID</code> を自動生成。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import uuid
from sqlalchemy import Column, String, Boolean, DateTime
from db import Base

class Users(Base):
    __tablename__ = "users"

    user_id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
    name = Column(String(100), nullable=False)
    email = Column(String(100), nullable=False, unique=True)
    password = Column(String(255), nullable=False)
    updated_by = Column(String(100))
    updated_at = Column(DateTime)
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">uuid</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">sqlalchemy</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Column</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">String</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Boolean</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">DateTime</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">db</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Base</span></span>
<span class="line"></span>
<span class="line"><span style="color: #8FBCBB">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Users</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">Base</span><span style="color: #D8DEE9FF">):</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">__tablename__</span><span style="color: #D8DEE9FF"> = </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">users</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">user_id</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">Column</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">(36)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">primary_key</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">True</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">lambda</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">str</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">uuid</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">uuid4</span><span style="color: #D8DEE9FF">()))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">Column</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">(100)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">nullable</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">False</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">email</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">Column</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">(100)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">nullable</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">False</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">unique</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">True</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">password</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">Column</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">(255)</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">nullable</span><span style="color: #D8DEE9FF">=</span><span style="color: #8FBCBB">False</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">updated_by</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">Column</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">String</span><span style="color: #D8DEE9FF">(100))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">updated_at</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">Column</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">DateTime</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"></span></code></pre></div>



<p>uuidについて、もっと知りたい方は下記の記事をどうぞ！</p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2025/07/10/uuid/" title="UUIDとは？主キーに使われる理由と連番との違いをわかりやすく解説" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/07/２行-1-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/２行-1-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-1-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-1-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-1-376x212.png 376w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">UUIDとは？主キーに使われる理由と連番との違いをわかりやすく解説</div><div class="blogcard-snippet internal-blogcard-snippet">UUIDはなぜ主キーに使われるのか？連番との違いやメリット・デメリット、UUIDの種類（v1〜v5）まで初心者向けにやさしく解説。PythonでのUUIDの生成方法も紹介します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
</div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc17">schemas/user.py：バリデーション用スキーマ定義</span></h3>



<p>フロントから送られてくる入力値（name, email, password）をバリデーションします。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>from pydantic import BaseModel

class UserCreate(BaseModel):
    name: str
    email: str
    password: str</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">pydantic</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">BaseModel</span></span>
<span class="line"></span>
<span class="line"><span style="color: #8FBCBB">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">UserCreate</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">BaseModel</span><span style="color: #D8DEE9FF">):</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">name</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">str</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">email</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">str</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">password</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">str</span></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc18">utils/common_fields.py：共通項目の自動付与</span></h3>



<p><code>updated_by</code> や <code>updated_at</code> などの共通項目を自動で付与します。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>from datetime import datetime, timezone, timedelta

def generate_common_fields():
    jst_now = datetime.now(timezone(timedelta(hours=9)))
    return {
        "updated_by": "system",
        "updated_at": jst_now,
    }
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">datetime</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">datetime</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">timezone</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">timedelta</span></span>
<span class="line"></span>
<span class="line"><span style="color: #8FBCBB">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">generate_common_fields</span><span style="color: #D8DEE9FF">():</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">jst_now</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">datetime</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">now</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">timezone</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">timedelta</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">hours</span><span style="color: #D8DEE9FF">=9)))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        &quot;</span><span style="color: #8FBCBB">updated_by</span><span style="color: #D8DEE9FF">&quot;: &quot;</span><span style="color: #8FBCBB">system</span><span style="color: #D8DEE9FF">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        &quot;</span><span style="color: #8FBCBB">updated_at</span><span style="color: #D8DEE9FF">&quot;: </span><span style="color: #8FBCBB">jst_now</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc19">routers/registration.py：登録処理ルーターの作成</span></h3>



<p>POSTされた情報をDBに保存するルーターです。辞書展開でスキーマ＋共通フィールドを結合します。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session　　// DBとのやりとりの型（Session）を指定
from db import SessionLocal
from models.user import Users
from schemas.user import UserCreate
from utils.common_fields import generate_common_fields

router = APIRouter()

// データベースとの接続を一時的に確立して、使い終わったら自動で切るための仕組み
def get_db():
    db = SessionLocal()  // DB接続を開始
    try:
        yield db　　 // データを登録・検索
    finally:
        db.close()　// DB接続を終了
        
@router.post("/registration")
def create_user(user: UserCreate, db: Session = Depends(get_db)):
    common_fields = generate_common_fields()

    db_user = Users(
        **user.dict(),
        **common_fields
    )

    db.add(db_user)
    db.commit()

    return {"message": "登録成功"}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fastapi</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">APIRouter</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Depends</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">sqlalchemy</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">orm</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Session</span><span style="color: #D8DEE9FF">　　</span><span style="color: #616E88">// DBとのやりとりの型（Session）を指定</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">db</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">SessionLocal</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">models</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">Users</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">schemas</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">user</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">UserCreate</span></span>
<span class="line"><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">utils</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">common_fields</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">generate_common_fields</span></span>
<span class="line"></span>
<span class="line"><span style="color: #8FBCBB">router</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">APIRouter</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// データベースとの接続を一時的に確立して、使い終わったら自動で切るための仕組み</span></span>
<span class="line"><span style="color: #8FBCBB">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">get_db</span><span style="color: #D8DEE9FF">():</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">db</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">SessionLocal</span><span style="color: #D8DEE9FF">()  </span><span style="color: #616E88">// DB接続を開始</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">try</span><span style="color: #D8DEE9FF">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">yield</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">db</span><span style="color: #D8DEE9FF">　　 </span><span style="color: #616E88">// データを登録・検索</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">finally</span><span style="color: #D8DEE9FF">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">db</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">close</span><span style="color: #D8DEE9FF">()　</span><span style="color: #616E88">// DB接続を終了</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span></span>
<span class="line"><span style="color: #D8DEE9FF">@</span><span style="color: #8FBCBB">router</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">post</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/registration</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #8FBCBB">def</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">create_user</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">user</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">UserCreate</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">db</span><span style="color: #D8DEE9FF">: </span><span style="color: #8FBCBB">Session</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">Depends</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">get_db</span><span style="color: #D8DEE9FF">)):</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">common_fields</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">generate_common_fields</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">db_user</span><span style="color: #D8DEE9FF"> = </span><span style="color: #8FBCBB">Users</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">**</span><span style="color: #8FBCBB">user</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">dict</span><span style="color: #D8DEE9FF">()</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">**</span><span style="color: #8FBCBB">common_fields</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">db</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">add</span><span style="color: #D8DEE9FF">(</span><span style="color: #8FBCBB">db_user</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">db</span><span style="color: #D8DEE9FF">.</span><span style="color: #8FBCBB">commit</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #8FBCBB">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF">&quot;</span><span style="color: #8FBCBB">message</span><span style="color: #D8DEE9FF">&quot;: &quot;</span><span style="color: #8FBCBB">登録成功</span><span style="color: #D8DEE9FF">&quot;</span><span style="color: #ECEFF4">}</span></span></code></pre></div>



<p>※ここで辞書化の記事をつくっていれよう！</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc20">トラブル対応：ログでFastAPIのエラーを確認する方法</span></h2>



<p>ログをリアルタイムで監視するには、以下のコマンドを使いましょう。</p>



<p><code>-f</code> は <code>--follow</code> の略で「ログを継続して追いかける」オプションです。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>docker logs -f コンテナ名</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9">docker</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">logs</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">f</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">コンテナ名</span></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>おつかれさまでした！</p>
</div></div><p>The post <a href="https://it-bokenki.com/2025/07/10/fastapi/">【初心者OK】FastAPI×MySQLをDockerで連携！Pythonで爆速開発する方法</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/07/10/fastapi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順</title>
		<link>https://it-bokenki.com/2025/07/03/docker-minio/</link>
					<comments>https://it-bokenki.com/2025/07/03/docker-minio/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Thu, 03 Jul 2025 13:26:50 +0000</pubDate>
				<category><![CDATA[開発ツール（Dev Tools）]]></category>
		<category><![CDATA[開発事例]]></category>
		<category><![CDATA[Docker]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=4194</guid>

					<description><![CDATA[<p>未経験からエンジニア転職を目指すなら&#x1f449; テックゲート転職 をチェック！ この記事の目的 Next.jsなどのアプリから、ローカルのMinIOにファイルを保存・取得したい人向けに、Docker環境へのMin [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/07/03/docker-minio/">Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>どもども<br>今回は「<strong>MinIOの導入</strong>」について解説します。</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-r sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/5-1-150x150.png" alt="バグヲ" class="speech-icon-image"/></figure><div class="speech-name">バグヲ</div></div><div class="speech-balloon">
<p>読み方はミニオ…？ なにそれ怖い…</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>こんなあなたにピッタリな記事&#x1f447;</p>



<div style="height:15px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li>ローカルでS3っぽい環境を作りたい</li>



<li>Docker上にストレージを立てたい</li>



<li>Next.jsやNode.jsからファイルを保存したい</li>
</ul>



<p>がまぁまぁわかります！</p>
</div></div>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=457GS4+E5N2LU+5D9I+HVV0H">
<img loading="lazy" decoding="async" border="0" width="300" height="250" alt="" src="https://www25.a8.net/svt/bgt?aid=250611124856&#038;wid=001&#038;eno=01&#038;mid=s00000025047003004000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www18.a8.net/0.gif?a8mat=457GS4+E5N2LU+5D9I+HVV0H" alt=""></div>



<p class="has-text-align-center">未経験からエンジニア転職を目指すなら<br>&#x1f449; <a href="https://px.a8.net/svt/ejp?a8mat=457GS4+E5N2LU+5D9I+HVFKY">テックゲート転職</a> をチェック！</p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together">
<a href="https://it-bokenki.com/2025/06/14/status/" title="404？500? ステータスコードとは？よく見るコード厳選まとめ" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-3-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-3-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-3-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-3-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">404？500? ステータスコードとは？よく見るコード厳選まとめ</div><div class="blogcard-snippet internal-blogcard-snippet">開発エンジニアが現場でよく使うHTTPステータスコードを、意味・原因・使われる場面とともにわかりやすく解説！</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
</div>



<h2 class="wp-block-heading"><span id="toc1">この記事の目的</span></h2>



<p>Next.jsなどのアプリから、<strong>ローカルのMinIOにファイルを保存・取得</strong>したい人向けに、<br>Docker環境へのMinIO導入と、接続手順を紹介します。<br>（AWSは使いません！）</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">MinIOとは？</span></h2>



<p>MinIO（ミニオ）は、<strong>Amazon S3と互換性のあるオブジェクトストレージ</strong>です。<br>S3と同じAPIを使えるため、<code>@aws-sdk/client-s3</code> を使って接続できます。</p>



<p><span class="keyboard-key">&#x1f4e6; MinIOの特徴</span></p>



<ul class="wp-block-list">
<li>軽量で高速なオブジェクトストレージ</li>



<li>Dockerで簡単に立ち上げられる</li>



<li>完全ローカルで完結する（S3の代替に便利）</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc3">実装方法</span></h2>



<h3 class="wp-block-heading"><span id="toc4">ステップ1：MinIOをDockerで立ち上げる</span></h3>



<p>まず、<code>docker-compose.yml</code> に以下を追加します。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>services:
  minio:
    image: minio/minio
    container_name: minio
    ports:
      - "9000:9000"  // APIアクセス用（アプリ・SDKから）
      - "9001:9001"  // 管理画面（GUI）
    environment:
      MINIO_ROOT_USER: your_username       // 開発用ユーザー名
      MINIO_ROOT_PASSWORD: your_password   // 開発用パスワード
    command: server /data --console-address ":9001"
    volumes:　// データ永続化の設定
      - minio_data:/data

volumes: // データ永続化の設定
  minio_data:
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">services</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">  minio</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">    image</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">minio</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">minio</span></span>
<span class="line"><span style="color: #D8DEE9FF">    container_name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">minio</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ports</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">9000:9000</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// APIアクセス用（アプリ・SDKから）</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">9001:9001</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// 管理画面（GUI）</span></span>
<span class="line"><span style="color: #D8DEE9FF">    environment</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      MINIO_ROOT_USER</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">your_username</span><span style="color: #D8DEE9FF">       </span><span style="color: #616E88">// 開発用ユーザー名</span></span>
<span class="line"><span style="color: #D8DEE9FF">      MINIO_ROOT_PASSWORD</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">your_password</span><span style="color: #D8DEE9FF">   </span><span style="color: #616E88">// 開発用パスワード</span></span>
<span class="line"><span style="color: #D8DEE9FF">    command</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">server</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">--</span><span style="color: #D8DEE9">console</span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9">address</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">:9001</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    volumes</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF">　</span><span style="color: #616E88">// データ永続化の設定</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> minio_data</span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">/</span><span style="color: #D8DEE9">data</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">volumes</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// データ永続化の設定</span></span>
<span class="line"><span style="color: #D8DEE9FF">  minio_data</span><span style="color: #ECEFF4">:</span></span>
<span class="line"></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x1f433; docker minio volume とは？</span></p>



<p>MinIOにアップロードしたファイルを、<strong>コンテナを削除・再起動しても消えないように保存する</strong>ための方法です。</p>



<p>通常、Dockerコンテナだけに保存したデータは、コンテナが停止・削除されると一緒に消えてしまいます。それを防ぐために、Dockerでは <code>volumes</code> を使って、<strong>データをPCの中（ホスト側）に残す仕組み</strong>が用意されています。</p>



<p>MinIOは <code>/data</code> にファイルを保存するため、上記（ // データ永続化の設定）のように設定しておけば、ファイルは消えません。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc5">ステップ2：MinIOにログイン＆バケット作成</span></h3>



<p>ブラウザで <code>http://localhost:9001</code> にアクセス<br>先ほど設定したユーザー名／パスワードでログイン</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="506" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.32.54-1024x506.png" alt="Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順" class="wp-image-4200" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.32.54-1024x506.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.32.54-300x148.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.32.54-768x379.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.32.54-1536x759.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.32.54-2048x1012.png 2048w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.32.54-1320x652.png 1320w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>ボタンをクリックでOKです。<br>これによりライセンス画面が閉じて、通常のMinIO管理画面（バケット作成など）に進めます。これは<strong>ローカル環境で使う分には問題ありません</strong>。</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="508" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-15.59.36-1024x508.png" alt="Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順" class="wp-image-4195" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-15.59.36-1024x508.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-15.59.36-300x149.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-15.59.36-768x381.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-15.59.36-1536x762.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-15.59.36-2048x1016.png 2048w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-15.59.36-1320x655.png 1320w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>＋create Bucket</code> を押し、「uploads」など任意のバケットを作成</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="501" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.00.16-1024x501.png" alt="Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順" class="wp-image-4196" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.00.16-1024x501.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.00.16-300x147.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.00.16-768x376.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.00.16-1536x752.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.00.16-2048x1003.png 2048w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.00.16-1320x646.png 1320w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>バケットを作成完了</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="506" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.01.46-1024x506.png" alt="Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順" class="wp-image-4197" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.01.46-1024x506.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.01.46-300x148.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.01.46-768x380.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.01.46-1536x759.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.01.46-2048x1012.png 2048w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-02-16.01.46-1320x652.png 1320w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc6">ステップ3：SDKをインストール</span></h3>



<p>MinIOはS3互換なので、以下をインストールします。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>npm install @aws-sdk/client-s3</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc7">ステップ4：ファイルアップロードの実装</span></h3>



<p><code>src/lib/minio.ts</code> を作成して、接続設定を実装</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3Client = new S3Client({
  region: "us-east-1", // MinIOでは使われないが、SDKの必須項目のため任意の値を指定（省略不可）
  endpoint: "http://localhost:9000",
  credentials: {
    accessKeyId: "your_username",     // 開発用ユーザー名
    secretAccessKey: "your_password", // 開発用パスワード
  },
  forcePathStyle: true, // バケット名をパスで扱うようにするためtrue
});

export async function uploadToMinio(file: File) {
  const arrayBuffer = await file.arrayBuffer();　// ファイルの生データを取得

  const command = new PutObjectCommand({
    Bucket: "uploads",       // GUIで作成したバケット名
    Key: file.name,          // 保存時の名前
    Body: arrayBuffer,       // アップロードするデータの中身
    ContentType: file.type,  // ファイルの種類（MIME）
  });

  await s3Client.send(command);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">S3Client</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">PutObjectCommand</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">@aws-sdk/client-s3</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">s3Client</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">S3Client</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">region</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">us-east-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// MinIOでは使われないが、SDKの必須項目のため任意の値を指定（省略不可）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">endpoint</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">http://localhost:9000</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">credentials</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">accessKeyId</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">your_username</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">     </span><span style="color: #616E88">// 開発用ユーザー名</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">secretAccessKey</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">your_password</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// 開発用パスワード</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">forcePathStyle</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// バケット名をパスで扱うようにするためtrue</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">uploadToMinio</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">file</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> File</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">arrayBuffer</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">arrayBuffer</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF">　</span><span style="color: #616E88">// ファイルの生データを取得</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">command</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">PutObjectCommand</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">Bucket</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">uploads</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">       </span><span style="color: #616E88">// GUIで作成したバケット名</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">Key</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">          </span><span style="color: #616E88">// 保存時の名前</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">Body</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">arrayBuffer</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">       </span><span style="color: #616E88">// アップロードするデータの中身</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">ContentType</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">type</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// ファイルの種類（MIME）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">s3Client</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">send</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">command</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc8">ステップ5：動作確認</span></h3>



<p>Next.jsやNode.jsの処理の中で <code>uploadToMinio(file)</code> を呼び出せば、<br>ローカルのMinIOにファイルが保存されます。</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="504" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-9.25.33-1024x504.png" alt="Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順" class="wp-image-4203" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-9.25.33-1024x504.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-9.25.33-300x148.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-9.25.33-768x378.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-9.25.33-1536x756.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-9.25.33-2048x1007.png 2048w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-9.25.33-1320x649.png 1320w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>Dockerもっと勉強したい！</strong>と思われた方は、こちらの記事もおすすめです。</p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2025/07/03/docker-mysql/" title="DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-18-376x212.png 376w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説</div><div class="blogcard-snippet internal-blogcard-snippet">DockerでMacにMySQLの開発環境を一瞬で構築！Apple Silicon対応のインストール手順、docker-compose.ymlと.env連携、utf8mb4設定、ターミナル操作でのDB作成まで初心者にもわかりやすく解説しています。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
</div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>おつかれさまでした！</p>
</div></div><p>The post <a href="https://it-bokenki.com/2025/07/03/docker-minio/">Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/07/03/docker-minio/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説</title>
		<link>https://it-bokenki.com/2025/07/03/docker-mysql/</link>
					<comments>https://it-bokenki.com/2025/07/03/docker-mysql/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Thu, 03 Jul 2025 13:24:22 +0000</pubDate>
				<category><![CDATA[開発ツール（Dev Tools）]]></category>
		<category><![CDATA[開発事例]]></category>
		<category><![CDATA[Docker]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=4158</guid>

					<description><![CDATA[<p>未経験でも気軽に！サブスク型プログラミングスクール【Freeks】 Dockerとは？ Dockerとは、「アプリを動かすための環境」をまるごとお弁当箱のようにパッケージにできるツールです。 メリット など、プログラミン [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/07/03/docker-mysql/">DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>どもども<br>今回は「<strong>DockerでMySQLをローカルに環境構築</strong>」について解説します。</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-r sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/5-1-150x150.png" alt="バグヲ" class="speech-icon-image"/></figure><div class="speech-name">バグヲ</div></div><div class="speech-balloon">
<p>どっかー…？ なんか難しそう…</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>こんなあなたにピッタリな記事&#x1f447;</p>



<div style="height:15px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li>開発環境を一瞬で作りたい</li>



<li>チームと同じ環境で作業したい</li>



<li>MacでMySQLをすぐに使えるようにしたい</li>
</ul>



<p>がまぁまぁわかります！</p>
</div></div>



<div style="height:21px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=457GS5+A4DB02+5JVK+BXQOH">
<img loading="lazy" decoding="async" border="0" width="300" height="250" alt="" src="https://www20.a8.net/svt/bgt?aid=250611125612&#038;wid=001&#038;eno=01&#038;mid=s00000025904002005000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www12.a8.net/0.gif?a8mat=457GS5+A4DB02+5JVK+BXQOH" alt=""></div>



<p class="has-text-align-center"><a href="https://px.a8.net/svt/ejp?a8mat=457GS5+A4DB02+5JVK+BX3J6">未経験でも気軽に！サブスク型プログラミングスクール【Freeks】</a></p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-default">
<a href="https://it-bokenki.com/2025/07/03/docker-minio/" title="Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-376x212.png 376w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順</div><div class="blogcard-snippet internal-blogcard-snippet">DockerでMinIOを構築し、Next.jsやNode.jsからS3互換のローカルストレージにファイルを保存・取得する方法を、初心者向けに手順付きでわかりやすく解説します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<h2 class="wp-block-heading"><span id="toc1">Dockerとは？</span></h2>



<p>Dockerとは、<strong>「アプリを動かすための環境」をまるごとお弁当箱のようにパッケージにできるツール</strong>です。</p>



<p><span class="keyboard-key">メリット</span></p>



<ul class="wp-block-list">
<li>&#x2705; 開発環境の構築が一瞬でできる</li>



<li>&#x2705; チームで同じ環境を再現できる</li>



<li>&#x2705; テスト・本番環境の切り替えも簡単</li>
</ul>



<p>など、プログラミング学習でも現場でも大活躍します。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">環境構築手順</span></h2>



<h3 class="wp-block-heading"><span id="toc3">ステップ0：Macのチップ確認（IntelかApple Siliconか）</span></h3>



<p>まず前提として、自分のMacの「チップの種類」を確認します。</p>



<p><span class="keyboard-key">&#x2714; 確認方法</span></p>



<ol class="wp-block-list">
<li>メニューバー左上の「（リンゴマーク）」をクリック</li>



<li>「このMacについて」を選ぶ</li>



<li>表示されるウィンドウで「チップ」の項目を確認</li>
</ol>



<div style="height:5px" aria-hidden="true" class="wp-block-spacer"></div>



<p>下記が表示された場合、Apple M4 Pro → <strong>Apple Silicon 用</strong>の Docker を選びましょう。<br></p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="550" height="618" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.26.35.png" alt="Docker MySQLをローカルに構築する完全手順｜Mac対応・日本語文字化け対策つき" class="wp-image-4199" style="width:326px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.26.35.png 550w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-03-8.26.35-267x300.png 267w" sizes="auto, (max-width: 550px) 100vw, 550px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc4">ステップ1：Docker Desktop のインストール</span></h3>



<p>① <a href="https://www.docker.com/products/docker-desktop/">Docker公式サイト</a> にアクセス</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>② 「<strong>Download for Mac – Apple Silicon</strong>」を選んでインストーラをダウンロード</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="511" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.38.43-1024x511.png" alt="Docker MySQLをローカルに構築する完全手順｜Mac対応・日本語文字化け対策つき" class="wp-image-4160" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.38.43-1024x511.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.38.43-300x150.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.38.43-768x383.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.38.43-1536x767.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.38.43-2048x1023.png 2048w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.38.43-1320x659.png 1320w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>③ ダウンロードされた<code>.dmg</code> を開き、「Docker.app」をアプリケーションフォルダに移動</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="477" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.40.08-1024x477.png" alt="Docker MySQLをローカルに構築する完全手順｜Mac対応・日本語文字化け対策つき" class="wp-image-4161" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.40.08-1024x477.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.40.08-300x140.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.40.08-768x358.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.40.08-1320x615.png 1320w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.40.08.png 1404w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc5">ステップ2：Docker初期セットアップと起動</span></h3>



<p>① アプリケーションから <code>Docker</code> を起動</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="632" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.42.04-1024x632.png" alt="Docker MySQLをローカルに構築する完全手順｜Mac対応・日本語文字化け対策つき" class="wp-image-4162" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.42.04-1024x632.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.42.04-300x185.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.42.04-768x474.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.42.04-1536x947.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.42.04-1320x814.png 1320w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.42.04.png 1592w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>「Docker サブスクリプション利用規約」への同意確認画面が表示されます。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x1f4cc; ざっくり要約</span></p>



<ul class="wp-block-list">
<li><strong>個人利用</strong> や <strong>小規模な開発会社</strong>での使用なら、<strong>無料で使えます！</strong>（AcceptしてOK）</li>



<li>商用利用で「社員250人超 or 年間収益1,000万ドル超」の場合は有料プランが必要</li>
</ul>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>② 「<strong>Use recommended settings</strong>」でOK</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="446" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.43.57-1024x446.png" alt="Docker MySQLをローカルに構築する完全手順｜Mac対応・日本語文字化け対策つき" class="wp-image-4163" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.43.57-1024x446.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.43.57-300x131.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.43.57-768x334.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.43.57-1536x668.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.43.57-1320x574.png 1320w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.43.57.png 1774w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>少し待つと、画面右上のメニューバーに「&#x1f433;クジラマーク」が表示されます。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc6">補足：Docker Desktop のアカウント登録 or ログイン画面</span></h3>



<p>Dockerアカウントを使うと、Docker Hub（イメージ共有サービス）と連携したり、CI/CD用に活用できたりします。</p>



<p>ただし、<strong>ローカルで開発するだけならアカウント登録は不要です！</strong><br>「Skip」をクリックしたら、いよいよDockerが使えるようになります&#x1f433;</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="568" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-1024x568.png" alt="Docker MySQLをローカルに構築する完全手順｜Mac対応・日本語文字化け対策つき" class="wp-image-4164" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-1024x568.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-300x166.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-768x426.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-1536x852.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-2048x1136.png 2048w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.45.08-1320x732.png 1320w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc7">ステップ3：MySQLコンテナ用のdocker-compose.ymlを準備</span></h3>



<p>下記は <code>docker-compose.yml</code> は MySQLコンテナをローカルに構築するための雛形になります<strong>。</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>version: '3.8'  // Docker Composeのバージョン

services:
  mysql:  // サービス名（任意）
    image: mysql:8.0   // 使用するMySQLのバージョン
    container_name: 任意コンテナの名前
    restart: always. // PC再起動後も自動で再起動する
    environment:  // MySQLの初期設定（環境変数）
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    ports:
      - "3306:3306" 
    volumes:
      - mysql_data:/var/lib/mysql  // データ永続化

volumes:
  mysql_data:   // データ永続化</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">version</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">3.8</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// Docker Composeのバージョン</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">services</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">  mysql</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// サービス名（任意）</span></span>
<span class="line"><span style="color: #D8DEE9FF">    image</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> mysql</span><span style="color: #ECEFF4">:</span><span style="color: #B48EAD">8.0</span><span style="color: #D8DEE9FF">   </span><span style="color: #616E88">// 使用するMySQLのバージョン</span></span>
<span class="line"><span style="color: #D8DEE9FF">    container_name</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">任意コンテナの名前</span></span>
<span class="line"><span style="color: #D8DEE9FF">    restart</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">always</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// PC再起動後も自動で再起動する</span></span>
<span class="line"><span style="color: #D8DEE9FF">    environment</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF">  </span><span style="color: #616E88">// MySQLの初期設定（環境変数）</span></span>
<span class="line"><span style="color: #D8DEE9FF">      MYSQL_ROOT_PASSWORD</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9">MYSQL_ROOT_PASSWORD</span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      MYSQL_DATABASE</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9">MYSQL_DATABASE</span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      MYSQL_USER</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9">MYSQL_USER</span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      MYSQL_PASSWORD</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">$</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9">MYSQL_PASSWORD</span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ports</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">3306:3306</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span></span>
<span class="line"><span style="color: #D8DEE9FF">    volumes</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> mysql_data</span><span style="color: #ECEFF4">:</span><span style="color: #81A1C1">/var</span><span style="color: #D8DEE9FF">/</span><span style="color: #D8DEE9">lib</span><span style="color: #D8DEE9FF">/mysql  </span><span style="color: #616E88">// データ永続化</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">volumes</span><span style="color: #ECEFF4">:</span></span>
<span class="line"><span style="color: #D8DEE9FF">  mysql_data</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF">   </span><span style="color: #616E88">// データ永続化</span></span></code></pre></div>



<p><strong>データ永続化とは？</strong>と思われた方は、下記の記事にて解説しています！</p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2025/07/03/docker-minio/" title="Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-160x90.png 160w, https://it-bokenki.com/wp-content/uploads/2025/07/２行-17-376x212.png 376w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">Next.js × Docker MinIO｜S3互換ストレージにファイルを保存する手順</div><div class="blogcard-snippet internal-blogcard-snippet">DockerでMinIOを構築し、Next.jsやNode.jsからS3互換のローカルストレージにファイルを保存・取得する方法を、初心者向けに手順付きでわかりやすく解説します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc8">ステップ4：.envファイルの準備</span></h3>



<p>上記の <code>docker-compose.yml</code> は <strong><code>.env</code> </strong>とセットで動作します。</p>



<p>同じフォルダに <code>.env</code> ファイルを作成して、以下のように記述してください。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>MYSQL_ROOT_PASSWORD=ルートパスワード
MYSQL_DATABASE=データベース名
MYSQL_USER=ユーザー名
MYSQL_PASSWORD=パスワード</code></pre></div>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="362" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.55.26-1024x362.png" alt="Docker MySQLをローカルに構築する完全手順｜Mac対応・日本語文字化け対策つき" class="wp-image-4165" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.55.26-1024x362.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.55.26-300x106.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.55.26-768x272.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.55.26-1536x543.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.55.26-2048x724.png 2048w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-14.55.26-1320x467.png 1320w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc9">ステップ5：MySQLコンテナの起動</span></h3>



<p> <code>docker-compose.yml</code> がある場所で下記を実行</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>docker compose up -d</code></pre></div>



<p>成功すると、MySQLが起動して <code>.env</code> の設定が反映された状態になります。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc10">ステップ6：MySQLコンテナに接続</span></h3>



<p>下記は、<strong>Docker コンテナ内の MySQL に接続して、対話的に操作するコマンド</strong>です。<br>パスワード聞かれたら <code>.env</code> に書いた <code>rootpass</code> を入力</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>docker exec -it 任意コンテナの名前 mysql -u root -p</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>ログインできたら次のコマンドで設定したデータベースがあるか確認</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>SHOW DATABASES;</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc11">ステップ7：文字化け防止（utf8mb4）</span></h3>



<p>Docker経由でMySQLに接続すると、文字コードが <code>latin1</code> になることがあります。</p>



<p>全角文字を扱うには <code>utf8mb4</code>にするには以下のようにオプションを追加して接続してください。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>docker exec -it　コンテナ名 mysql -u root -pパスワード --default-character-set=utf8mb4</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>その後のセッション内では、<code>latin1</code> が <code>utf8mb4</code> に変更されています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>SET NAMES utf8mb4;</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><span class="keyboard-key">&#x26a0;&#xfe0f; 注意点</span></p>



<p><code>exit;</code> すると元に戻るため、必要に応じて <code>my.cnf</code> に設定を加えるか、SQLスクリプトで自動化しましょう。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc12">ターミナルでテーブルを作ってみよう</span></h2>



<p>DockerでMySQLを使う場合は、基本的にターミナル（CLI）で操作します。<br>ただし、GUIで操作したい場合も、別のツールを使えば可能です。<br>今回は<strong>ターミナルで実行する例</strong>を紹介します。<br></p>



<p>① MySQLのプロンプトに接続</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>docker exec -it　コンテナ名 mysql -u root -pパスワード --default-character-set=utf8mb4</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>② 接続後の表示</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>mysql&gt;</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>③ 以下のSQLで、<code>users</code> テーブルを作成</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100),
  email VARCHAR(100)
);</code></pre></div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>④  <code>SHOW TABLES;</code> を実行すると、テーブルが作成されたことを確認できます。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-15.04.36-1024x200.png" alt="Docker MySQLをローカルに構築する完全手順｜Mac対応・日本語文字化け対策つき" class="wp-image-4167" srcset="https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-15.04.36-1024x200.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-15.04.36-300x59.png 300w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-15.04.36-768x150.png 768w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-15.04.36-1536x301.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-15.04.36-1320x258.png 1320w, https://it-bokenki.com/wp-content/uploads/2025/07/スクリーンショット-2025-07-01-15.04.36.png 1604w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><strong>SQL（クエリ）とは？</strong>と思われた方は、下記の記事にて解説しています！</p>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2023/05/20/sql/" title="SQLの概要と主要なコマンド： データベース操作の基本を学ぼう！" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2023/05/SQL-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2023/05/SQL-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2023/05/SQL-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2023/05/SQL-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SQLの概要と主要なコマンド： データベース操作の基本を学ぼう！</div><div class="blogcard-snippet internal-blogcard-snippet">SQLの概要と主要なコマンドについて解説します。SQLはデータベース操作において重要です。データベース操作の基本を理解し、データの効率的な操作を行いましょう！</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-normal-card">
<a href="https://it-bokenki.com/2023/05/21/sql-create/" title="SQLのCREATE文の基礎 : テーブルの作成をマスターしよう！" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2023/05/SQL-1-2-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2023/05/SQL-1-2-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2023/05/SQL-1-2-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2023/05/SQL-1-2-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">SQLのCREATE文の基礎 : テーブルの作成をマスターしよう！</div><div class="blogcard-snippet internal-blogcard-snippet">SQLのCREATE文の基礎をマスターすることで、データベース操作の幅が広がり、より高度なデータベースの開発や管理ができるようになります。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc13">補足：Docker操作の基本コマンド</span></h2>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>docker ps -a                 # コンテナ一覧（停止中も）
docker stop コンテナ名        # 停止
docker rm コンテナ名          # 削除
docker images               # イメージ一覧
docker rmi イメージID         # イメージ削除
docker volume ls            # ボリューム一覧</code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>おつかれさまでした！</p>
</div></div><p>The post <a href="https://it-bokenki.com/2025/07/03/docker-mysql/">DockerでMySQLをローカル構築｜Mac対応・CLI操作と文字化け対策も解説</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/07/03/docker-mysql/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Azure Blob Storage にファイルをアップロードする方法【Next.js × React】</title>
		<link>https://it-bokenki.com/2025/06/26/azure-blob/</link>
					<comments>https://it-bokenki.com/2025/06/26/azure-blob/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Thu, 26 Jun 2025 04:07:14 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Next.js]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[クラウド]]></category>
		<category><![CDATA[開発事例]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=3579</guid>

					<description><![CDATA[<p>Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち 全体構成 Azure 側の準備 ファイルを保存する先として Azure Blob Storage を使うために、まずはAzure側での [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/26/azure-blob/">Azure Blob Storage にファイルをアップロードする方法【Next.js × React】</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>どもども<br>今回は「Azure Blob Storage にファイルをアップロードする方法」について解説します。</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-r sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/5-1-150x150.png" alt="バグヲ" class="speech-icon-image"/></figure><div class="speech-name">バグヲ</div></div><div class="speech-balloon">
<p>Azureって最近よく使われてるやつだよね？<br>でも、いまだによくわかってない…</p>
</div></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>こんなあなたにピッタリな記事&#x1f447;</p>



<div style="height:15px" aria-hidden="true" class="wp-block-spacer"></div>



<ul class="wp-block-list">
<li>Azure側の設定がわからない</li>



<li>APIはどのように記載するの？</li>



<li>とりあえず実装だけ知りたい！</li>
</ul>



<p>がまぁまぁわかります！</p>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="text-align: center;"><a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NZROH">
<img loading="lazy" decoding="async" border="0" width="300" height="250" alt="" src="https://www27.a8.net/svt/bgt?aid=230528653907&#038;wid=001&#038;eno=01&#038;mid=s00000023513004030000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www11.a8.net/0.gif?a8mat=3T911P+F006GI+51FE+NZROH" alt=""></div>



<p class="has-text-align-center"><a href="https://px.a8.net/svt/ejp?a8mat=3T911P+F006GI+51FE+NWRNM">Instagramフォロワー数9,500人を越える人気のもち・大福店 えにかいたもち</a></p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-default">
<a href="https://it-bokenki.com/2025/06/24/npm-vs-pnpm-vs-yarn/" title="npm・yarn・pnpmの違いとは？速度や特徴をざっくり比較！" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-1-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-1-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-1-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-12-1-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">npm・yarn・pnpmの違いとは？速度や特徴をざっくり比較！</div><div class="blogcard-snippet internal-blogcard-snippet">JavaScriptやTypeScriptのプロジェクトでは、依存関係の管理に「パッケージマネージャー」が欠かせません。この記事では、代表的な3つのパッケージマネージャー「npm」「yarn」「pnpm」の違いを比較し、それぞれのメリット・デメリットを表にまとめて解説します。npmはNode.js公式が提供している標準的なツールで、ほとんどのプロジェクトで使われています。対してyarnはMeta（旧Facebook）が開発したもので、npmよりも速くて安定していると評価されています。そして近年注目されているのがpnpmで、圧倒的なインストール速度とディスク効率の良さから、徐々にシェアを伸ばしています。選び方に迷ったときは、プロジェクトの規模や開発チームの方針を基準にするとよいでしょう。たとえば、モノレポ構成や高速なCI/CDを重視する場合はpnpmが向いています。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">全体構成</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="784" height="432" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.00.26.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3587" style="width:483px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.00.26.png 784w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.00.26-300x165.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.00.26-768x423.png 768w" sizes="auto, (max-width: 784px) 100vw, 784px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">Azure 側の準備</span></h2>



<p>ファイルを保存する先として <code>Azure Blob Storage</code> を使うために、まずはAzure側での初期設定を行いましょう。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc3">STEP1：ストレージアカウントを作成</span></h3>



<ol class="wp-block-list">
<li><a class="" href="https://portal.azure.com/">Azureポータル</a> にログイン</li>



<li>左上の「≡」メニューから「<strong>ストレージアカウント</strong>」を選択し、「<strong>＋作成</strong>」をクリック</li>



<li>以下の<span class="bold-red">※</span><span class="red">1</span>ように項目を入力（最低限でOK）</li>



<li>下部の「<strong>確認および作成</strong>」→「<strong>作成</strong>」を押す</li>



<li>デプロイ完了後、「<strong>リソースに移動</strong>」ボタンをクリック</li>
</ol>



<p><span class="bold-red">※</span><span class="red">1</span></p>



<figure class="wp-block-table is-style-regular"><table class="has-fixed-layout"><thead><tr><th>項目</th><th>設定値</th></tr></thead><tbody><tr><td>サブスクリプション</td><td>任意（通常は1つ）</td></tr><tr><td>リソースグループ</td><td>任意（新規作成でもOK）</td></tr><tr><td>ストレージアカウント名</td><td>例：myuploadstorage123（一意な名前）</td></tr><tr><td>リージョン</td><td>お住まいに近い場所（東日本ならJapan East）</td></tr><tr><td>パフォーマンス</td><td>standard</td></tr><tr><td>冗長性</td><td>ローカル冗長ストレージ (LRS)</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc4">STEP2：Blobコンテナーを作成</span></h3>



<ol class="wp-block-list">
<li>ストレージアカウントの画面左メニューから「<strong>データストレージ</strong>」→「<strong>コンテナー</strong>」を選択</li>



<li>上部の「＋<strong>コンテナー</strong>」ボタンを押して以下の<span class="bold-red">※</span><span class="red">2</span>ように項目を入力</li>



<li>「<strong>作成</strong>」ボタンを押す</li>
</ol>



<p><span class="bold-red">※</span><span class="red">2</span></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>項目</th><th>設定値</th></tr></thead><tbody><tr><td>名前</td><td>例：upload</td></tr><tr><td>パブリックアクセスレベル</td><td>プライベート（推奨）</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc5">STEP3：接続文字列を取得して .env.local に設定</span></h3>



<ol class="wp-block-list">
<li>ストレージアカウントの左メニューをスクロールし、「<strong>セキュリティとネットワーク</strong>」セクションにある「<strong>アクセスキー</strong>」をクリック</li>



<li>「key1」の項目にある「<strong>接続文字列</strong>」をコピー</li>



<li>プロジェクト直下の <code>.env.local</code> ファイルに以下を追加</li>
</ol>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=（コピーした内容）
AZURE_CONTAINER_NAME=（設定した名前）</code></pre></div>



<p><code>.env.local</code> を変更したあとは、<strong>Next.js の開発サーバーを再起動</strong>してください。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc6">ファイルアップロード用UIの作成（React）</span></h2>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>&#8220;use client&#8221;;
import { useState, useRef } from &#8220;react&#8221;;
export default function DragAndDropUploader() {
  // ドラッグ中かどうかの状態
  const [isDragging, setIsDragging] = useState(false);
  // メッセージ表示用（成功・エラー）
  const [message, setMessage] = useState(&#8220;&#8221;);
  // input要素の参照（隠してボタンから開く用）
  const fileInputRef = useRef&lt;HTMLInputElement>(null);
  // 選択されたファイル一覧
  const [selectedFiles, setSelectedFiles] = useState&lt;File[]>([]);
  // ファイルがドロップされた時の処理
  const handleDrop = (e: React.DragEvent&lt;HTMLDivElement>) => {
    e.preventDefault();
    setIsDragging(false);
    const files = Array.from(e.dataTransfer.files); // FileList → 配列に変換
    if (files.length === 0) return;
    setSelectedFiles(files);
  };
  // ドラッグ中（枠が赤くなる）
  const handleDragOver = (e: React.DragEvent&lt;HTMLDivElement>) => {
    e.preventDefault();
    setIsDragging(true);
  };
  // ドラッグが外れたとき（赤枠を戻す）
  const handleDragLeave = () => setIsDragging(false);
  // ファイル選択時の処理
  const handleFileSelect = (e: React.ChangeEvent&lt;HTMLInputElement>) => {
    const files = Array.from(e.target.files || []);
    if (files.length === 0) return;
    setSelectedFiles(files);
  };
  // 一括取消（ファイルクリア）
  const handleClearFiles = () => {
    setSelectedFiles([]);
    setMessage(&#8220;&#8221;);
  };
  // ボタンクリック → input をクリックさせる
  const handleButtonClick = () => {
    fileInputRef.current?.click();
  };
  // アップロード処理
  const handleUpload = async () => {
    if (selectedFiles.length === 0) {
      setMessage(&#8220;&#x274c; ファイルが選択されていません&#8221;);
      return;
    }
    const formData = new FormData();
    selectedFiles.forEach((file) => formData.append(&#8220;files&#8221;, file));
    try {
      // APIにPOST
      const res = await fetch(&#8220;/api/upload&#8221;, {
        method: &#8220;POST&#8221;,
        body: formData,
      });
      if (res.ok) {
        setMessage(&#8220;&#x2705; アップロード成功しました&#8221;);
      } else {
        const data = await res.json();
        setMessage(&#8220;&#x274c; アップロード失敗: &#8221; + (data.error || res.statusText));
      }
    } catch {
      setMessage(&#8220;&#x274c; アップロード中にエラーが発生しました&#8221;);
    }
  };
  return (
    &lt;div className=&#8221;m-5 mx-auto w-fit&#8221;>
      &lt;h2>&#x1f4e5; スキルシートをアップロード&lt;/h2>
      {/* ドラッグ＆ドロップ枠 */}
      &lt;div
        onDrop={handleDrop}
        onDragOver={handleDragOver}
        onDragLeave={handleDragLeave}
        className={`w-[620px] h-[320px] border-2 border-dashed rounded p-5 text-center transition-colors duration-300 ${
          isDragging ? &#8220;border-red-500&#8221; : &#8220;border-gray-300&#8221;
        } bg-gray-100 flex items-center justify-center`}
      >
        &lt;div>
          &lt;p>{isDragging ? &#8220;ここにドロップ&#8221; : &#8220;ファイルをドラッグ＆ドロップ&#8221;}&lt;/p>
          &lt;p>または&lt;/p>
          &lt;button
            onClick={handleButtonClick}
            className=&#8221;bg-red-500 text-white px-4 py-2 rounded&#8221;
          >
            ファイルを選択
          &lt;/button>
        &lt;/div>
        {/* ファイル選択用input（非表示） */}
        &lt;input
          type=&#8221;file&#8221;
          multiple
          ref={fileInputRef}
          onChange={handleFileSelect}
          style={{ display: &#8220;none&#8221; }}
        />
      &lt;/div>
      {/* 選択されたファイルの表示 + ✕ボタン */}
      {selectedFiles.length > 0 &amp;&amp; (
        &lt;div className=&#8221;mt-4 text-left&#8221;>
          &lt;div className=&#8221;flex items-start justify-between mb-2&#8243;>
            &lt;p className=&#8221;font-semibold&#8221;>選択中のファイル&lt;/p>
            &lt;button
              onClick={handleClearFiles}
              className=&#8221;font-semibold text-sm text-red-600 hover:underline&#8221;
            >
              一括取消
            &lt;/button>
          &lt;/div>
          &lt;ul>
            {selectedFiles.map((file, index) => (
              &lt;li key={index} className=&#8221;flex items-center justify-between mb-1&#8243;>
                &lt;span>{file.name}&lt;/span>
                &lt;button
                  onClick={() =>
                    setSelectedFiles((prev) =>
                      prev.filter((_, i) => i !== index)
                    )
                  }
                  className=&#8221;text-red-600 font-bold&#8221;
                >
                  ✕
                &lt;/button>
              &lt;/li>
            ))}
          &lt;/ul>
        &lt;/div>
      )}
      {/* アップロードボタン */}
      &lt;div>
        &lt;button
          onClick={handleUpload}
          className=&#8221;bg-green-700 text-white px-4 py-2 rounded mt-5&#8243;
        >
          アップロード
        &lt;/button>
        {message &amp;&amp; &lt;p className=&#8221;mt-2&#8243;>{message}&lt;/p>}
      &lt;/div>
    &lt;/div>
  );
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">use client</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">useState</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">useRef</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">react</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">default</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">DragAndDropUploader</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ドラッグ中かどうかの状態</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">isDragging</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setIsDragging</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">false</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// メッセージ表示用（成功・エラー）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">message</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setMessage</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// input要素の参照（隠してボタンから開く用）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">fileInputRef</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useRef</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLInputElement</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">null</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 選択されたファイル一覧</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">[</span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">setSelectedFiles</span><span style="color: #ECEFF4">]</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">useState</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">File[]</span><span style="color: #ECEFF4">&gt;</span><span style="color: #D8DEE9FF">([])</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ファイルがドロップされた時の処理</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleDrop</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">DragEvent</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLDivElement</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">preventDefault</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setIsDragging</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">false</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Array</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">from</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">dataTransfer</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// FileList → 配列に変換</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">files</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">return;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setSelectedFiles</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ドラッグ中（枠が赤くなる）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleDragOver</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">DragEvent</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLDivElement</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">preventDefault</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setIsDragging</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">true</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ドラッグが外れたとき（赤枠を戻す）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleDragLeave</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">setIsDragging</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">false</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ファイル選択時の処理</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleFileSelect</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">e</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> React</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">ChangeEvent</span><span style="color: #ECEFF4">&lt;</span><span style="color: #D8DEE9FF">HTMLInputElement</span><span style="color: #ECEFF4">&gt;)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Array</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">from</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">e</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">target</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">||</span><span style="color: #D8DEE9FF"> [])</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">files</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">return;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setSelectedFiles</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// 一括取消（ファイルクリア）</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleClearFiles</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setSelectedFiles</span><span style="color: #D8DEE9FF">([])</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// ボタンクリック → input をクリックさせる</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleButtonClick</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">fileInputRef</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">current</span><span style="color: #ECEFF4">?.</span><span style="color: #88C0D0">click</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">  </span><span style="color: #616E88">// アップロード処理</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">handleUpload</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length </span><span style="color: #81A1C1">===</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#x274c; ファイルが選択されていません</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">FormData</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">forEach</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">append</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">files</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// APIにPOST</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetch</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">/api/upload</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">method</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">POST</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">body</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">ok</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#x2705; アップロード成功しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">else</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">data</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#x274c; アップロード失敗: </span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">data</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">||</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">res</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">statusText</span><span style="color: #D8DEE9FF">))</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">setMessage</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">&#x274c; アップロード中にエラーが発生しました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">m-5 mx-auto w-fit</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;h2&gt;</span><span style="color: #D8DEE9FF">&#x1f4e5; スキルシートをアップロード</span><span style="color: #81A1C1">&lt;/h2&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #616E88">/* ドラッグ＆ドロップ枠 */</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;div</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">onDrop</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleDrop</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">onDragOver</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleDragOver</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">onDragLeave</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleDragLeave</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">`</span><span style="color: #A3BE8C">w-[620px] h-[320px] border-2 border-dashed rounded p-5 text-center transition-colors duration-300 </span><span style="color: #81A1C1">${</span></span>
<span class="line"><span style="color: #A3BE8C">          </span><span style="color: #D8DEE9">isDragging</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">?</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">border-red-500</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">:</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">border-gray-300</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #A3BE8C">        </span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C"> bg-gray-100 flex items-center justify-center</span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;p&gt;{</span><span style="color: #D8DEE9">isDragging</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ここにドロップ</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ファイルをドラッグ＆ドロップ</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">}&lt;/p&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;p&gt;</span><span style="color: #D8DEE9FF">または</span><span style="color: #81A1C1">&lt;/p&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleButtonClick</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">bg-red-500 text-white px-4 py-2 rounded</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            ファイルを選択</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">{</span><span style="color: #616E88">/* ファイル選択用input（非表示） */</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;input</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">type</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">file</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">multiple</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">ref</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">fileInputRef</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">onChange</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleFileSelect</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">style</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">display</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">none</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">/&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #616E88">/* 選択されたファイルの表示 + ✕ボタン */</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length </span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mt-4 text-left</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;div</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">flex items-start justify-between mb-2</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;p</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">font-semibold</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF">選択中のファイル</span><span style="color: #81A1C1">&lt;/p&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleClearFiles</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">font-semibold text-sm text-red-600 hover:underline</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">              一括取消</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;ul&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">{</span><span style="color: #D8DEE9">selectedFiles</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">map</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">index</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> (</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #81A1C1">&lt;li</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">key</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">index</span><span style="color: #81A1C1">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">flex items-center justify-between mb-1</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">&lt;span&gt;{</span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">}&lt;/span&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    </span><span style="color: #88C0D0">setSelectedFiles</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">prev</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                      </span><span style="color: #D8DEE9">prev</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">filter</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">_</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=&gt;</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">i</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">!==</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">index</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">                    )</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  </span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">text-red-600 font-bold</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">                  ✕</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">              </span><span style="color: #81A1C1">&lt;/li&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">            ))</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #81A1C1">&lt;/ul&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      )</span><span style="color: #81A1C1">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">{</span><span style="color: #616E88">/* アップロードボタン */</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;button</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">onClick</span><span style="color: #81A1C1">={</span><span style="color: #D8DEE9">handleUpload</span><span style="color: #81A1C1">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">          </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">bg-green-700 text-white px-4 py-2 rounded mt-5</span><span style="color: #ECEFF4">&quot;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">          アップロード</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">&lt;/button&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">{</span><span style="color: #D8DEE9">message</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&amp;&amp;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;p</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">className</span><span style="color: #81A1C1">=</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">mt-2</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">&gt;{</span><span style="color: #D8DEE9">message</span><span style="color: #81A1C1">}&lt;/p&gt;}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">&lt;/div&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc7">ファイルアップロードAPIの作成（Next.js × Azure SDK）</span></h2>



<p><span class="badge-grey">仕様</span><br>ファイル名が一意になるように、アップロードされたファイル名の末尾に<br>「<strong>_YYYYMMDD_hhmm</strong>」と日時を付与してから<code>Azure Blob Storage</code> にアップロードする</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>import { NextResponse } from &#8220;next/server&#8221;;
import { BlobServiceClient } from &#8220;@azure/storage-blob&#8221;;
// POSTメソッドで呼び出されたときの処理
export async function POST(req: Request) {
  try {
    // フロントから送られてきたファイル付きのデータ（FormData）を受け取る
    const formData = await req.formData();
    // name=&#8221;files&#8221; という名前で送られてきたファイル全部取得
    const files = formData.getAll(&#8220;files&#8221;) as File[];
    // ファイルが1つもなかったらエラーを返す
    if (!files.length) {
      return NextResponse.json(
        { error: &#8220;ファイルがありません&#8221; },
        { status: 400 }
      );
    }
    // Azure Storage に接続するためのクライアントを作成
    const blobService = BlobServiceClient.fromConnectionString(
      process.env.AZURE_STORAGE_CONNECTION_STRING! // .env.local に設定した接続文字列
    );
    // コンテナー（事前に作成済み）に接続
    const container = blobService.getContainerClient(
      process.env.AZURE_CONTAINER_NAME!
    );
    // ファイルごとに繰り返す
    for (const file of files) {
      // タイムスタンプを付けて、ファイル名の重複を防ぐ
      const now = new Date();
      const timestamp = `${now.getFullYear()}${String(
        now.getMonth() + 1
      ).padStart(2, &#8220;0&#8221;)}${String(now.getDate()).padStart(2, &#8220;0&#8221;)}_${String(
        now.getHours()
      ).padStart(2, &#8220;0&#8221;)}${String(now.getMinutes()).padStart(2, &#8220;0&#8221;)}`;
      // 元のファイル名と拡張子を分離
      const originalName = file.name;
      const ext = originalName.includes(&#8220;.&#8221;)
        ? &#8220;.&#8221; + originalName.split(&#8220;.&#8221;).pop()
        : &#8220;&#8221;;
      const base = originalName.replace(ext, &#8220;&#8221;);
      // 一意なファイル名を作成（例：resume_20250626_1030.pdf）
      const uniqueName = `${base}_${timestamp}${ext}`;
      // Fileオブジェクト → ArrayBuffer に変換
      const arrayBuffer = await file.arrayBuffer();
      // ArrayBuffer → Node.js の Buffer に変換（Azure用）
      const buffer = Buffer.from(arrayBuffer);
      // Azure上の「保存先ファイル（Blob）」を指定
      const blockBlob = container.getBlockBlobClient(uniqueName);
      // ファイルをアップロード（bufferとして保存）
      await blockBlob.uploadData(buffer);
    }
    // 成功レスポンスを返す
    return NextResponse.json({
      message: &#8220;すべてのファイルをアップロードしました&#8221;,
    });
  } catch (error) {
    // エラーが起きた場合はログを出してエラーを返す
    console.error(&#8220;アップロードエラー:&#8221;, error);
    return NextResponse.json({ error: &#8220;アップロード失敗&#8221; }, { status: 500 });
  }
}
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">NextResponse</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">next/server</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">BlobServiceClient</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">from</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">@azure/storage-blob</span><span style="color: #ECEFF4">&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// POSTメソッドで呼び出されたときの処理</span></span>
<span class="line"><span style="color: #81A1C1">export</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">async</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">function</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">POST</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">req</span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> Request</span><span style="color: #ECEFF4">)</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// フロントから送られてきたファイル付きのデータ（FormData）を受け取る</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">req</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">formData</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// name=&quot;files&quot; という名前で送られてきたファイル全部取得</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">formData</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getAll</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">files</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">as</span><span style="color: #D8DEE9FF"> File[]</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// ファイルが1つもなかったらエラーを返す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9">files</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9FF">length) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextResponse</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">error</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">ファイルがありません</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">status</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">400</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #D8DEE9FF">      )</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// Azure Storage に接続するためのクライアントを作成</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">blobService</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">BlobServiceClient</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">fromConnectionString</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">AZURE_STORAGE_CONNECTION_STRING</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// .env.local に設定した接続文字列</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// コンテナー（事前に作成済み）に接続</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">container</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">blobService</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getContainerClient</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">AZURE_CONTAINER_NAME</span><span style="color: #81A1C1">!</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// ファイルごとに繰り返す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">for</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">of</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">files</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// タイムスタンプを付けて、ファイル名の重複を防ぐ</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">now</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">new</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">Date</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">timestamp</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getFullYear</span><span style="color: #ECEFF4">()</span><span style="color: #81A1C1">}${</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #A3BE8C">        </span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getMonth</span><span style="color: #ECEFF4">()</span><span style="color: #A3BE8C"> </span><span style="color: #81A1C1">+</span><span style="color: #A3BE8C"> </span><span style="color: #B48EAD">1</span></span>
<span class="line"><span style="color: #A3BE8C">      </span><span style="color: #ECEFF4">).</span><span style="color: #88C0D0">padStart</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">}${</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getDate</span><span style="color: #ECEFF4">()).</span><span style="color: #88C0D0">padStart</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">_</span><span style="color: #81A1C1">${</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">(</span></span>
<span class="line"><span style="color: #A3BE8C">        </span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getHours</span><span style="color: #ECEFF4">()</span></span>
<span class="line"><span style="color: #A3BE8C">      </span><span style="color: #ECEFF4">).</span><span style="color: #88C0D0">padStart</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">}${</span><span style="color: #88C0D0">String</span><span style="color: #ECEFF4">(</span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getMinutes</span><span style="color: #ECEFF4">()).</span><span style="color: #88C0D0">padStart</span><span style="color: #ECEFF4">(</span><span style="color: #B48EAD">2</span><span style="color: #ECEFF4">,</span><span style="color: #A3BE8C"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">)</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// 元のファイル名と拡張子を分離</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">originalName</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">name</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">ext</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">originalName</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">includes</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">?</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">originalName</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">split</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">.</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">pop</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">base</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">originalName</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">replace</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">ext</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;&quot;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// 一意なファイル名を作成（例：resume_20250626_1030.pdf）</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">uniqueName</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">base</span><span style="color: #81A1C1">}</span><span style="color: #A3BE8C">_</span><span style="color: #81A1C1">${</span><span style="color: #D8DEE9">timestamp</span><span style="color: #81A1C1">}${</span><span style="color: #D8DEE9">ext</span><span style="color: #81A1C1">}</span><span style="color: #ECEFF4">`</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// Fileオブジェクト → ArrayBuffer に変換</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">arrayBuffer</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">file</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">arrayBuffer</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// ArrayBuffer → Node.js の Buffer に変換（Azure用）</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">buffer</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">Buffer</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">from</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">arrayBuffer</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// Azure上の「保存先ファイル（Blob）」を指定</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">blockBlob</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">container</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">getBlockBlobClient</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">uniqueName</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">      </span><span style="color: #616E88">// ファイルをアップロード（bufferとして保存）</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">blockBlob</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">uploadData</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">buffer</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// 成功レスポンスを返す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextResponse</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">      </span><span style="color: #88C0D0">message</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">すべてのファイルをアップロードしました</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">catch</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">) </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #ECEFF4">    </span><span style="color: #616E88">// エラーが起きた場合はログを出してエラーを返す</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">console</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">error</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">アップロードエラー:</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">error</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">NextResponse</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">json</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">error</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">アップロード失敗</span><span style="color: #ECEFF4">&quot;</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">status</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">500</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span></span>
<span class="line"><span style="color: #ECEFF4">}</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc8">実行画面</span></h2>



<p><code>Azure Blob Storage</code> にまだファイルがアップロードされていない状態</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-1024x180.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3597" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-1024x180.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-300x53.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-768x135.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-1536x270.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.02-2048x360.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>アップロード画面</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="591" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-1024x591.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3592" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-1024x591.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-300x173.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-768x443.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.03.37.png 1490w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>ファイルをアップロードすると、選択中のファイルが表示される</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="694" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35-1024x694.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3598" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35-1024x694.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35-300x203.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35-768x521.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.35.png 1496w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>アップロードボタンを押下すると、アップロード完了</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="737" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50-1024x737.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3599" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50-1024x737.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50-300x216.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50-768x552.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.15.50.png 1518w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p><code>Azure Blob Storage</code> にファイルが格納されていることを確認</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="145" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-1024x145.png" alt="Azure Blob Storage にファイルをアップロードする方法【Next.js × React】" class="wp-image-3600" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-1024x145.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-300x42.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-768x108.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-1536x217.png 1536w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-26-11.16.21-2048x289.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="てんハロ運営者" class="speech-icon-image"/></figure><div class="speech-name">てんハロ運営者</div></div><div class="speech-balloon">
<p>おつかれさまでした！</p>
</div></div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-together is-style-default">
<a href="https://it-bokenki.com/2025/06/24/dom-vs-virtual-dom/" title="DOMと仮想DOMの違いをわかりやすく比較！" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/２行-13-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/２行-13-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-13-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/２行-13-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">DOMと仮想DOMの違いをわかりやすく比較！</div><div class="blogcard-snippet internal-blogcard-snippet">DOMと仮想DOMの違いを、お弁当のたとえや図を使ってやさしく解説！Reactでよく出る用語の理解がグッと深まります。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="height:0px" aria-hidden="true" class="wp-block-spacer"></div><p>The post <a href="https://it-bokenki.com/2025/06/26/azure-blob/">Azure Blob Storage にファイルをアップロードする方法【Next.js × React】</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/26/azure-blob/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SendGrid で自動返信メールを送信する方法</title>
		<link>https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/</link>
					<comments>https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/#respond</comments>
		
		<dc:creator><![CDATA[てんハロ運営者]]></dc:creator>
		<pubDate>Wed, 11 Jun 2025 02:18:18 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[バックエンド]]></category>
		<category><![CDATA[開発事例]]></category>
		<category><![CDATA[make]]></category>
		<category><![CDATA[Next.js]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[SendGrid]]></category>
		<category><![CDATA[SMTP]]></category>
		<category><![CDATA[ローコードツール]]></category>
		<category><![CDATA[自動送信メール]]></category>
		<guid isPermaLink="false">https://it-bokenki.com/?p=2850</guid>

					<description><![CDATA[<p>でじぼうです。 API：SendGridと、ローコードツール：Makeをかけ合わせた自動返信メールを送信する方法についてご説明します。 SendGridとは？ 「メール配信を専門に行ってくれるクラウドサービス（SaaS） [&#8230;]</p>
<p>The post <a href="https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/">SendGrid で自動返信メールを送信する方法</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></description>
										<content:encoded><![CDATA[<p></p>



<p>でじぼうです。</p>



<p>API：SendGridと、ローコードツール：Makeをかけ合わせた自動返信メールを送信する方法についてご説明します。</p>



<div class="wp-block-cocoon-blocks-balloon-ex-box-1 speech-wrap sb-id-1 sbs-stn sbp-l sbis-cb cf block-box"><div class="speech-person"><figure class="speech-icon"><img decoding="async" src="https://it-bokenki.com/wp-content/uploads/2023/05/名称未設定のデザイン-1-1-150x150.png" alt="でじぼう" class="speech-icon-image"/></figure><div class="speech-name">でじぼう</div></div><div class="speech-balloon">
<p>この記事は下記の方がおすすめ！<br></p>



<ul class="wp-block-list">
<li style="font-size:15px">SendGridってなに？</li>



<li style="font-size:15px">Makeってなに？</li>



<li style="font-size:15px">API×ローコードツールの実装方法が知りたい！</li>
</ul>
</div></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<div style="text-align: center;">
<a rel="nofollow" href="https://px.a8.net/svt/ejp?a8mat=3TB6RG+C3TBLE+4RIG+BYT9D">
<img loading="lazy" decoding="async" border="0" width="468" height="60" alt="" src="https://www26.a8.net/svt/bgt?aid=230629372732&#038;wid=001&#038;eno=01&#038;mid=s00000022228002010000&#038;mc=1"></a>
<img loading="lazy" decoding="async" border="0" width="1" height="1" src="https://www16.a8.net/0.gif?a8mat=3TB6RG+C3TBLE+4RIG+BYT9D" alt="">
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc1">SendGridとは？</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/4.png" alt="SendGrid" class="wp-image-2870" style="width:445px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/4.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/4-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p><strong>「メール配信を専門に行ってくれるクラウドサービス（SaaS）」</strong> です。</p>



<p>開発したWebサービスやアプリケーションからSendGridにメールを送るように設定するだけで、SendGridがそのメールをユーザーに届けてくれます。</p>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-memo block-box">
<p class="has-small-font-size"><span class="marker">SaaS（サース）</span>：完成されたアプリをインターネット経由で利用できるサービス</p>
</div>



<div class="wp-block-cocoon-blocks-blogcard blogcard-type bct-reference-link is-style-normal-card">
<a href="https://it-bokenki.com/2025/06/11/what-is-sendgrid/" title="メール配信の強い味方「SendGrid」を徹底解説！" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img loading="lazy" decoding="async" width="320" height="180" src="https://it-bokenki.com/wp-content/uploads/2025/06/1-320x180.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/1-320x180.png 320w, https://it-bokenki.com/wp-content/uploads/2025/06/1-120x68.png 120w, https://it-bokenki.com/wp-content/uploads/2025/06/1-160x90.png 160w" sizes="auto, (max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">メール配信の強い味方「SendGrid」を徹底解説！</div><div class="blogcard-snippet internal-blogcard-snippet">SendGridとは何かを初心者向けにわかりやすく解説。メール配信APIの仕組みや使い方、Webアプリとの連携方法を紹介します。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://it-bokenki.com" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">it-bokenki.com</div></div></div></div></a>
<p></p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc2">Makeとは？</span></h2>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/3.png" alt="make" class="wp-image-2869" style="width:408px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/3.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/3-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p>「<strong>ノーコードでさまざまなWebサービスを自動連携できるプラットフォーム（iPaaS）」です。</strong><br><br>Gmail、Slack、Google Sheets、Notionなどと連携し、ワークフローを視覚的に構築可能。<br>エンジニアでなくても効率的な自動化が行えます。<br></p>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-memo block-box">
<p class="has-small-font-size"><span class="marker">iPaaS（アイパース）</span>＝いろんなWebサービスをつなぐ橋渡し役</p>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc3">なぜ SendGrid × Makeで実装？</span></h2>



<p>もともと <span class="marker-under"><strong>フロントエンド × バックエンドAPI × Make</strong></span> を使ってお問い合わせフォームを実装していました。次のステップとして「お問い合わせをしてくれた方へ自動返信メールを送る機能」を追加することにしました。</p>



<p class="has-text-align-center"><span class="fz-20px"><span class="fz-22px"><span class="fz-24px">前回までの実装内容</span></span></span></p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/6.png" alt="Make SendGrid" class="wp-image-2899" style="width:704px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/6.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/6-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h5 class="wp-block-heading"><span>▼ 当初の選択肢：Make × 自前サーバー</span></h5>



<p>まずは自前で契約しているレンタルサーバーを使って実装しようとしましたが、Make のメール送信元である <span class="marker-under"><code>ie.</code><strong>ドメインをそのサーバーが受け付けない仕様であったため、自動返信が正常に動作しないという問題が発生</strong></span>しました。<br></p>



<h5 class="wp-block-heading"><span>▼ 次の選択肢：Make × Googleメール</span></h5>



<p>次に、Gmailを使う方法を検討しましたが、会社独自のドメインでGmailを使うにはGoogle Workspaceの有料契約が必要です。<br>今回はコストをできるだけ抑えたかったため、固定費が発生するこの案は見送りにしました。<br></p>



<h5 class="wp-block-heading"><span>&#x2705; 最終的な選択肢： SendGrid</span></h5>



<p class="has-text-align-center"><span class="fz-20px"><span class="fz-22px"><span class="fz-24px">これから</span>やる実装内容</span></span></p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/5.png" alt="Make SendGrid" class="wp-image-2900" style="width:700px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/5.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/5-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p>上記のような経緯があり、<span class="marker-under">無料で使えて独自ドメインも対応可能なSendGrid</span>を使うことに決定しました。<br>SendGridであれば、MakeからのSMTP連携も簡単で、自動返信メールも安定して送ることができます。</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading"><span id="toc4">SendGrid × Make 実装手順</span></h2>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="500" height="200" src="https://it-bokenki.com/wp-content/uploads/2025/06/Make.png" alt="Make SendGrid" class="wp-image-2851" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/Make.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/Make-300x120.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p>ここからは、実際にSendGridとMakeを使って自動返信メールを実装する手順を紹介していきます。</p>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc5">① SendGridアカウント作成と会社情報の登録</span></h3>



<p>まずはSendGridのアカウントを作成し、会社情報を登録します。<br>この登録内容の審査には1〜2営業日程度かかるので、余裕を持って申請しておきましょう。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc6">② 承認完了後、マイページにログインする</span></h3>



<p>承認されると、登録したメールアドレス宛に通知が届きます。<br>メールに記載されたリンクから送信者認証を完了させましょう。<br>その後、アカウントIDが発行されるので、事前に設定したパスワードでSendGridにログインします。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc7">③ 送信者情報の登録</span></h3>



<figure class="wp-block-image aligncenter size-full is-resized is-style-default"><img loading="lazy" decoding="async" width="500" height="500" src="https://it-bokenki.com/wp-content/uploads/2025/06/Make-1.png" alt="SendGrid 設定方法" class="wp-image-2853" style="width:478px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/Make-1.png 500w, https://it-bokenki.com/wp-content/uploads/2025/06/Make-1-300x300.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/Make-1-150x150.png 150w, https://it-bokenki.com/wp-content/uploads/2025/06/Make-1-100x100.png 100w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p>左側のDashbord  ＞「Create sender identity」をクリックし、送信者情報を登録します。<br>このとき、<strong><span class="marker-under">送信先・送信元のメールアドレスは同じでも問題ありません</span></strong>。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc8">④ APIキーの作成</span></h3>



<p>左メニューの「<strong><code>Settings ＞ API Keys</code></strong>」から「<strong><code>Create API Key</code></strong>」を選択し、新しいAPIキーを発行します。</p>



<figure class="wp-block-image size-large has-custom-border"><img loading="lazy" decoding="async" width="1024" height="726" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40-1024x726.png" alt="SendGrid 設定方法" class="wp-image-2855" style="border-width:1px" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40-1024x726.png 1024w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40-300x213.png 300w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40-768x545.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-16.56.40.png 1390w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>設定項目</th><th>設定内容</th></tr></thead><tbody><tr><td>API Key Name</td><td>任意の名前（例：Make用自動返信）</td></tr><tr><td>API Key Permissions</td><td><code>Full Access</code><br>（または&nbsp;<code>Restricted Access</code>&nbsp;→&nbsp;<code>Mail Send</code>&nbsp;にチェック）</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>「<code>Create &amp; View Key</code>」をクリックするとAPIキーが表示されます。<br><strong><span class="marker-under">このタイミングでしかコピーできない</span></strong>ので、忘れずに控えておきましょう。<br>後ほどMakeのSMTP接続設定で、このAPIキーをパスワードとして使います。</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc9">⑤ MakeでのSMTP接続設定方法</span></h3>



<p>Makeで「<code>Send an Email</code>」モジュールを追加し、以下の情報で新規<code>Connection</code>を作成します。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="800" height="882" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.04.46.png" alt="Make Email設定方法" class="wp-image-2856" style="width:376px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.04.46.png 800w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.04.46-272x300.png 272w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.04.46-768x847.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /></figure>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>設定項目</th><th>設定内容</th></tr></thead><tbody><tr><td>Connection type</td><td>Other（SMTP)</td></tr><tr><td>Connection name</td><td>任意の名前（例：SendGrid SMTP）</td></tr><tr><td>Email provider</td><td>Other</td></tr><tr><td>Email address</td><td>自動送信メールを送る主のメールアドレス</td></tr><tr><td>Your full name</td><td>会社名等</td></tr><tr><td>SMTP server</td><td>smtp.sendgrid.netを入力</td></tr><tr><td>Port</td><td><code>587</code>（推奨）または&nbsp;<code>465</code>（SSL）</td></tr><tr><td>Use a secure connection (TLS)</td><td>YES</td></tr><tr><td>Use explicit TLS</td><td>NO</td></tr><tr><td>User name</td><td>apikeyと文字を入力</td></tr><tr><td>Password</td><td>SendGridで生成したAPIキーを入力</td></tr></tbody></table></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc10">⑥ Fromアドレスの設定（詳細設定）</span></h3>



<p>SMTP設定が完了したら、次に<span class="marker-under"><strong>送信元メールアドレス（From）を設定</strong></span>します。<br>これはMakeの「<strong><code>Show advanced settings</code></strong>」をONにすることで表示される詳細設定欄にあります。<br>ここに正しいFromアドレスを入力しないと、メールが正しく送信できなかったり、送信者名が表示されなかったりします。<br><br>下記の写真は「<code>Show advanced settings</code>」がOFFになっている状態</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="617" height="1024" src="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54-617x1024.png" alt="Make Email設定方法" class="wp-image-2858" style="width:357px;height:auto" srcset="https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54-617x1024.png 617w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54-181x300.png 181w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54-768x1275.png 768w, https://it-bokenki.com/wp-content/uploads/2025/06/スクリーンショット-2025-06-10-17.09.54.png 884w" sizes="auto, (max-width: 617px) 100vw, 617px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc11">⑦ コードの実装</span></h3>



<p>今回下記のように、<ruby><code><rt>Node.js</rt></code></ruby>で実装しました。<br>環境変数は <code>.env</code> ファイルに定義し、以下を読み込んで使用しています。</p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>環境変数項目</th><th>値</th></tr></thead><tbody><tr><td>SLACK_WEBHOOK_URL</td><td>Makeとの連携URL</td></tr></tbody></table></figure>



<div style="height:10px" aria-hidden="true" class="wp-block-spacer"></div>



<p>Makeとの連携URLを設定して、Makeに送りたいお問い合わせ情報（name , email , phone, message)を設定します。</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><textarea class="code-block-pro-copy-button-textarea" aria-hidden="true" readonly>await fetch(process.env.SLACK_WEBHOOK_URL!, {
  method: &#8220;POST&#8221;,
  headers: {
    &#8220;Content-Type&#8221;: &#8220;application/json&#8221;,
  },
  body: JSON.stringify({
    name,
    email,
    phone,
    message,
  }),
});
</textarea><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">await</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">fetch</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">process</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">env</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">SLACK_WEBHOOK_URL</span><span style="color: #81A1C1">!</span><span style="color: #ECEFF4">,</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">method</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">POST</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">headers</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">Content-Type</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&quot;</span><span style="color: #A3BE8C">application/json</span><span style="color: #ECEFF4">&quot;</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">},</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #88C0D0">body</span><span style="color: #ECEFF4">:</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">JSON</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">stringify</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">name</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">email</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">phone</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #D8DEE9">message</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">,</span></span>
<span class="line"><span style="color: #ECEFF4">}</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span></code></pre></div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><span id="toc12">⑧ 設定完了</span></h3>



<p>送信者認証とSMTPの接続設定がすべて完了すると、Make上で自動返信メールが送信できるようになります。<br>これで、お問い合わせをしてくれた方に、即座に確認メールを送る仕組みが完成です。</p><p>The post <a href="https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/">SendGrid で自動返信メールを送信する方法</a> first appeared on <a href="https://it-bokenki.com">てんハロ｜未経験エンジニアのIT学習ログ</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://it-bokenki.com/2025/06/11/sendgrid-smtp-setup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
