Skip to content

Latest commit

 

History

History
666 lines (589 loc) · 17.2 KB

File metadata and controls

666 lines (589 loc) · 17.2 KB
marp true
theme default
paginate true
header APIハンズオン
style section { font-family: "Hiragino Sans", "Yu Gothic", sans-serif; font-size: 30px; padding: 60px; } section.lead { text-align: center; justify-content: center; } section.lead h1 { font-size: 66px; } section.big { justify-content: center; text-align: center; } section.big p { font-size: 52px; line-height: 1.6; font-weight: bold; } section.chapter { background: #1f2933; color: #fff; justify-content: center; text-align: center; } section.chapter h1 { color: #fff; font-size: 60px; } section.chapter p { color: #9aa5b1; } h1 { color: #1f2933; } h2 { color: #1f2933; border-bottom: 3px solid #4dc0b5; padding-bottom: 12px; } strong { color: #0b8f82; } h1 strong, h2 strong, h3 strong, h4 strong { color: #0b8f82; } code { font-size: 0.9em; } pre { font-size: 24px; line-height: 1.5; } table { font-size: 26px; margin: 0 auto; } th, td { padding: 13px 18px; } td:first-child, th:first-child { white-space: nowrap; } td code { white-space: nowrap; } .step { display: inline-block; background: #4dc0b5; color: #fff; border-radius: 50%; width: 52px; height: 52px; line-height: 52px; text-align: center; font-weight: bold; margin-right: 12px; } .note { font-size: 24px; color: #616e7c; margin-top: 36px; } .note.tight { margin-top: 18px; } /* --- 図用 --- */ .row { display: flex; align-items: center; justify-content: center; gap: 40px; margin-top: 46px; } .row.tight { margin-top: 26px; gap: 30px; } .box { border: 4px solid #7b8794; border-radius: 16px; padding: 26px 32px; text-align: center; font-size: 29px; font-weight: bold; color: #3e4c59; min-width: 240px; } .box .label { font-size: 21px; font-weight: normal; color: #7b8794; display: block; margin-bottom: 14px; } .box.api { border-color: #0b8f82; background: #f0fdfa; color: #0b8f82; } .box.api .label { color: #0b8f82; } .inner { background: #f5f7fa; border-radius: 12px; padding: 16px 22px; font-size: 25px; color: #3e4c59; margin-top: 14px; } .arrow { font-size: 56px; color: #4dc0b5; font-weight: bold; } .arrowlabel { text-align: center; color: #0b8f82; font-size: 22px; font-weight: bold; } .arrowlabel .arrow { display: block; line-height: 1; } .dim { opacity: 0.35; } /* --- 手順リスト --- */ .steps { margin-top: 26px; } .steps div { font-size: 27px; color: #3e4c59; line-height: 1.95; } .steps strong { color: #0b8f82; } .steps span { color: #7b8794; font-size: 23px; } .steps div.on { color: #0b8f82; font-weight: bold; } .ng { color: #e12d39; font-weight: bold; } /* --- 強調用ハイライト --- */ .steps div strong.hl, strong.hl { background: #4dc0b5; color: #fff; padding: 4px 14px; border-radius: 7px; font-weight: bold; font-size: 28px; } /* --- スクリーンショット --- */ .shot { margin-top: 28px; text-align: center; } .shot img { max-height: 300px; max-width: 100%; border: 3px solid #cbd2d9; border-radius: 10px; } /* --- server.js が2種類を返す図 --- */ .split { margin-top: 40px; text-align: center; } .split .stop { display: inline-block; border: 4px solid #0b8f82; background: #f0fdfa; border-radius: 14px; padding: 16px 46px; font-size: 30px; font-weight: bold; color: #0b8f82; } .split .sarrows { display: flex; justify-content: center; gap: 330px; font-size: 34px; color: #4dc0b5; line-height: 1.1; margin: 6px 0 2px; } .split .srow { display: flex; justify-content: center; gap: 36px; } .split .sbox { border: 3px solid #9aa5b1; border-radius: 12px; padding: 18px 24px; width: 380px; text-align: center; } .split .sbox.api { border-color: #0b8f82; background: #f0fdfa; } .split .sbox .stitle { display: block; font-size: 24px; font-weight: bold; color: #3e4c59; margin-bottom: 12px; } .split .sbox.api .stitle { color: #0b8f82; } .split .sbox .sitems { display: block; font-size: 21px; color: #7b8794; line-height: 1.7; } /* --- 三層構造との対比 --- */ .layers { margin-top: 44px; } .lrow { display: flex; align-items: center; gap: 9px; margin-bottom: 40px; } .lrow .llabel { width: 168px; font-size: 23px; font-weight: bold; color: #9aa5b1; flex-shrink: 0; } .lrow.on .llabel { color: #0b8f82; } .lrow .lbox { border: 3px solid #cbd2d9; border-radius: 10px; padding: 12px 14px; font-size: 22px; font-weight: bold; color: #7b8794; text-align: center; line-height: 1.25; } .lrow .lbox em { display: block; font-size: 17px; font-style: normal; font-weight: normal; color: #9aa5b1; margin-top: 3px; } .lrow.on .lbox { border-color: #0b8f82; background: #f0fdfa; color: #0b8f82; } .lrow.on .lbox em { color: #0b8f82; } .lrow .larrow { font-size: 22px; color: #4dc0b5; font-weight: bold; } /* --- 利点の予告 --- */ .benefits { margin-top: 66px; } .benefits div { font-size: 38px; font-weight: bold; color: #3e4c59; margin-bottom: 38px; } .benefits .bnum { display: inline-block; background: #4dc0b5; color: #fff; font-size: 25px; border-radius: 8px; padding: 7px 18px; margin-right: 22px; vertical-align: middle; } /* --- 略語の分解 --- */ .acronym { margin-top: 44px; } .arow { display: flex; align-items: baseline; gap: 0; margin-bottom: 24px; } .arow .ini { font-size: 68px; font-weight: bold; color: #4dc0b5; line-height: 1; width: 52px; } .arow .eng { font-size: 40px; font-weight: bold; color: #3e4c59; letter-spacing: 0.01em; width: 300px; } .arow .jp { margin-left: 24px; font-size: 30px; color: #616e7c; } .arow.on .ini { color: #0b8f82; } .arow.on .eng { color: #0b8f82; } .arow.on .jp { color: #0b8f82; font-weight: bold; } .sum { margin-top: 6px; padding-top: 26px; border-top: 3px solid #cbd2d9; font-size: 34px; font-weight: bold; color: #3e4c59; text-align: center; } .sum strong { color: #0b8f82; } /* --- 窓口の図(窓口 = サーバー側の表面。線1本で描く) --- */ .io { display: flex; align-items: flex-end; justify-content: center; gap: 0; margin-top: 56px; } .io .ioside { border: 4px solid #7b8794; border-radius: 16px; padding: 42px 30px; font-size: 28px; font-weight: bold; color: #3e4c59; min-width: 190px; text-align: center; } /* サーバー側。左辺だけを太くして、それを窓口として見せる */ .io .ioside.api { border: 4px solid #0b8f82; border-left: 14px solid #4dc0b5; background: #f0fdfa; color: #0b8f82; } .io .arrows { width: 300px; padding: 0 14px 14px; } .io .ioline { display: flex; flex-direction: column; align-items: center; margin: 14px 0; } .io .iolabel { font-size: 24px; font-weight: bold; color: #0b8f82; } .io .ioarrow { font-size: 30px; color: #4dc0b5; letter-spacing: -2px; line-height: 1.1; } .io .gatelabel { font-size: 22px; font-weight: bold; color: #0b8f82; margin-left: -6px; margin-bottom: 10px; white-space: nowrap; }

APIハンズオン


1. APIとは何か


名前がそのまま意味

Application アプリケーションを
Programming プログラムから使うための
Interface 窓口
アプリをプログラムから使うための窓口

インターフェース = 窓口

ブラウザ
決まった形で頼む ──────▶
◀────── 答えが返る
↓ この面が窓口 = API
server.js
inter(間) + face(面) = 間にある面 厚みのない1枚の面
窓口はサーバー側の表面 銀行の窓口が銀行の一部なのと同じ

窓口があると うれしいことが2つ

利点1 中身を知らなくても使える
利点2 見せたくないものを隠せる

利点1 中身を知らなくても使える

裏側でやっていること 自分が書くこと
天気 観測データを集めて スパコンで計算 fetch("/weather")
地図 膨大な画像から その地点を切り出す fetch("/map")
翻訳 大量の文章で学習したモデルで推論 fetch("/translate")
左は知らなくていい 書くのは右の1行だけ
自分で気象観測をしなくても 天気を出すアプリがつくれる

利点2 見せたくないものを隠せる

ブラウザ側 api.html / api.js
開発者ツールで
誰でも中身が見える
API
サーバー側 server.js
中身は
誰にも見えない
パスワードの照合 APIキー データベースの情報は 全部この右側に置く
この見えない側をバックエンドと呼ぶ

窓口のルールは この4つ

これがルール 今日つくるもの
どのURLか /auth
どのメソッドか POST
何を渡すか password
何が返るか { ok, message }
この4つが決まっていれば 別々の人がつくっても繋がる
②のGETとPOSTが 前の章でやったやつ

4つを組み合わせると コードになる

// server.js
if (req.method === "POST" && pathname === "/auth") {
//      ② メソッド                     ① URL

  const body = await req.json();
//      ③ 送られたもの を受け取る

  return Response.json({ ok: true, message: "ログインできました" });
//                     ④ 返すもの
}
今日書くのは これだけ
API を増やすときも ①②③④を決めるだけ

2. server.jsは
全部の入口


これがテンプレートの最初の形

import { serveDir } from "jsr:@std/http/file-server";

Deno.serve((req) => {                    // 通信は全部ここに来る
  const pathname = new URL(req.url).pathname;
  console.log(pathname);                 // 叩かれたパスを表示

  if (req.method === "GET" && pathname === "/welcome-message") {
    return new Response("jigインターンへようこそ!");
  }

  return serveDir(req, { fsRoot: "public", ... });
});
テンプレートから始まったときの server.js
書き換えたのは中身だけ 形はこのまま残っている

上から順に「自分の担当か」を見ている

通信が来た
1 GETで /welcome-message か? → バックエンドの情報 を返す
2 どれでもない → serveDirpublic の中のファイル を返す
一番下の serveDir は「どれにも当たらなかったとき」の受け皿

public の中のファイルも server.js が返している

server.js
public の中のファイル index.html / styles.css
index.js
バックエンドの情報 /welcome-message
の文字
画面をつくる部品も データも 同じ入口から 同じ形で返る
だから 1ファイルで両方できている

1つずつ 4回に分けて返る

叩かれたURL 上から順に見た結果 返ってきたもの
/ どれにも当たらない → serveDir index.html
/styles.css どれにも当たらない → serveDir CSS
/index.js どれにも当たらない → serveDir JavaScript
/welcome-message 1番目のifに当たった 「ようこそ」の文字
3回は public の中のファイル 1回は バックエンドの情報
どちらも同じ server.js が 同じ形で返している

自分のサイトで見てみよう

1 開発者ツール(F12)→ Network タブ
2 ページを再読み込みすると 4行ならぶ
NetworkタブにHTML CSS JS APIの4行が並んでいる
Initiator を見ると 誰が呼んだかが分かる
styles.cssindex.js(index) から つまりHTMLが呼んでいる

3. APIハンズオン


デプロイしたアプリに 足していく

1 クローンしたフォルダを開く 例: deno-app
2 server.js に窓口を1つ足す
3 直すたびに push する 今日は自分のPCでは動かさない デプロイ先のURLで確かめる
4 デプロイ先のURLに /api.html を付けると スマホからも使える
作業するのは自分のリポジトリ
教材リポジトリ(intern-dev-tutorialではない

進め方

1 サーバー側に書く server.js
2 ブラウザ側を新しくつくる api.html / api.js
3 push する
4 /api.html を開いて ボタンで確かめる
保存しただけでは 反映されない 動いているのはGitHubに置いたコード

今日さわるファイルは3つ

ファイル 今日の扱い
server.js return serveDir(上に足す
public/api.html 新しくつくる 今日の画面
public/api.js 新しくつくる 今日書くJSは全部ここ
もとの index.html触らない 別のページをつくる
serveDir より下に書くとたどりつかないので 必ずその上

READMEへ!