toshiki-notebook/development/aws/handson-bashoutter.html

449 lines
174 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en-US" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Hands-on #6: Bashoutter | Toshiki's Note</title>
<meta name="description" content="Toshiki's web notebook served via Vitepress!">
<link rel="preload stylesheet" href="/assets/style.1276ae12.css" as="style">
<script type="module" src="/assets/app.b7ebe66f.js"></script>
<link rel="preload" href="/assets/inter-roman-latin.2ed14f66.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="modulepreload" href="/assets/chunks/framework.ade46834.js">
<link rel="modulepreload" href="/assets/chunks/theme.2274efad.js">
<link rel="modulepreload" href="/assets/chunks/bashoutter_2.cd8a2a95.js">
<link rel="modulepreload" href="/assets/development_aws_handson-bashoutter.md.f49bb080.lean.js">
<link rel="stylesheet" href="https://cdnjs.toshiki.dev/ajax/libs/KaTeX/0.16.0/katex.min.css">
<link rel="stylesheet" href="https://cdnjs.toshiki.dev/ajax/libs/font-awesome/6.3.0/css/all.min.css">
<link rel="icon" href="https://r2.toshiki.dev/cdn/toshiki-notebook-favicon/favicon.ico">
<meta name="author" content="Anda Toshiki">
<meta name="keywords" content="Toshiki, Anda Toshiki, andatoshiki, GitHub, GitHub action, Vitepress, Vite, Notebook, Knowledge base, Programming, Programming Notes, Academic, Personal, Notebook, Productivity, Journal, Note-taking, Markdown, Notepad, Organization, Tutorial">
<meta name="google-site-verification" content="lm7PNJiYSPEx1dMast1Xptc0Vk0cU06o-daZSsIgr2I">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#3c8772">
<meta property="og:type" content="website">
<meta property="og:locale" content="en-US">
<meta property="og:title" content="Toshiki&#39;s Note">
<meta property="og:description" content="Toshiki&#39;s web notebook served via Vitepress!">
<meta property="og:site" content="https://note.toshiki.dev">
<meta property="og:site_name" content="Toshiki&#39;s Note">
<meta property="og:image" content="https://note.toshiki.dev/og-cover.png">
<script>function siteruntime(){window.setTimeout("siteruntime()",1e3),X=new Date("8/24/2021 10:28:00"),Y=new Date,T=Y.getTime()-X.getTime(),M=24*60*60*1e3,a=T/M,A=Math.floor(a),b=(a-A)*24,B=Math.floor(b),c=(b-B)*60,C=Math.floor((b-B)*60),D=Math.floor((c-C)*60),siteruntime_span.innerHTML="This site has been running for: "+A+" day(s) "+B+" hour(s) "+C+" minute(s) "+D+" second(s)"}siteruntime();</script>
<script async="true" defer="true" data-website-id="86de8554-d4c9-4f2b-b62a-068b71241048" src="https://umami.toshiki.dev/umami.js"></script>
<script id="check-dark-light">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
</head>
<body>
<div id="app"><div class="Layout" data-v-83f63849><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0eca8f1e></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0eca8f1e> Skip to content </a><!--]--><!----><header class="VPNav" data-v-83f63849 data-v-999a1a39><div class="VPNavBar has-sidebar" data-v-999a1a39 data-v-e99cf6bd><div class="container" data-v-e99cf6bd><div class="title" data-v-e99cf6bd><div class="VPNavBarTitle has-sidebar" data-v-e99cf6bd data-v-a8886b70><a class="title" href="/" data-v-a8886b70><!--[--><!--]--><!--[--><img class="VPImage logo" src="/logos/logo.png" alt data-v-164d1caf><!--]--><!--[-->Toshiki&#39;s Note<!--]--><!--[--><!--]--></a></div></div><div class="content" data-v-e99cf6bd><div class="curtain" data-v-e99cf6bd></div><div class="content-body" data-v-e99cf6bd><!--[--><!--]--><div class="VPNavBarSearch search" style="--vp-meta-key:&#39;Meta&#39;;" data-v-e99cf6bd><!--[--><div id="docsearch"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg class="DocSearch-Search-Icon" width="20" height="20" viewBox="0 0 20 20" aria-label="search icon"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-e99cf6bd data-v-1817056a><span id="main-nav-aria-label" class="visually-hidden" data-v-1817056a>Main Navigation</span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/development/" tabindex="0" data-v-1817056a data-v-f28b94cc data-v-075865b7><!--[-->Development<!--]--><!----></a><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-1817056a data-v-4c03a652><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-4c03a652><span class="text" data-v-4c03a652><!----> Academic <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-4c03a652><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-4c03a652><div class="VPMenu" data-v-4c03a652 data-v-da053c30><div class="items" data-v-da053c30><!--[--><!--[--><div class="VPMenuGroup" data-v-da053c30 data-v-2e982fbb><p class="title" data-v-2e982fbb>K-12</p><!--[--><!--[--><div class="VPMenuLink" data-v-2e982fbb data-v-6f715184><a class="VPLink link" href="/academic/chemistry/index" data-v-6f715184 data-v-075865b7><!--[-->Chemistry<!--]--><!----></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-2e982fbb data-v-6f715184><a class="VPLink link" href="/discrete-math/index" data-v-6f715184 data-v-075865b7><!--[-->Discrete Math.<!--]--><!----></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-2e982fbb data-v-6f715184><a class="VPLink link" href="/academic/literature/index" data-v-6f715184 data-v-075865b7><!--[-->Literature<!--]--><!----></a></div><!--]--><!--]--></div><!--]--><!--[--><div class="VPMenuGroup" data-v-da053c30 data-v-2e982fbb><p class="title" data-v-2e982fbb>Tools</p><!--[--><!--[--><div class="VPMenuLink" data-v-2e982fbb data-v-6f715184><a class="VPLink link" href="/academic/physics/ipho-formulas-jpn/1" data-v-6f715184 data-v-075865b7><!--[-->Formulas for IPhO JPN.<!--]--><!----></a></div><!--]--><!--]--></div><!--]--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><span class="VPLink" data-v-6f715184 data-v-075865b7><!--[--><!--]--><!----></span></div><!--]--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><span class="VPLink" data-v-6f715184 data-v-075865b7><!--[--><!--]--><!----></span></div><!--]--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><span class="VPLink" data-v-6f715184 data-v-075865b7><!--[--><!--]--><!----></span></div><!--]--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><span class="VPLink" data-v-6f715184 data-v-075865b7><!--[--><!--]--><!----></span></div><!--]--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><span class="VPLink" data-v-6f715184 data-v-075865b7><!--[--><!--]--><!----></span></div><!--]--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><span class="VPLink" data-v-6f715184 data-v-075865b7><!--[--><!--]--><!----></span></div><!--]--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><span class="VPLink" data-v-6f715184 data-v-075865b7><!--[--><!--]--><!----></span></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-1817056a data-v-4c03a652><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-4c03a652><span class="text" data-v-4c03a652><!----> Application <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-4c03a652><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-4c03a652><div class="VPMenu" data-v-4c03a652 data-v-da053c30><div class="items" data-v-da053c30><!--[--><!--[--><div class="VPMenuGroup" data-v-da053c30 data-v-2e982fbb><p class="title" data-v-2e982fbb>Personal projects</p><!--[--><!--[--><div class="VPMenuLink" data-v-2e982fbb data-v-6f715184><a class="VPLink link" href="/application/markdown-it-katex/how-to-use" data-v-6f715184 data-v-075865b7><!--[-->markdown-it-katex<!--]--><!----></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-2e982fbb data-v-6f715184><a class="VPLink link" href="/application/vitepress-plugin-shiki-twoslash/index" data-v-6f715184 data-v-075865b7><!--[-->vitepress-plugin-shiki-twoslash<!--]--><!----></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-1817056a data-v-4c03a652><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-4c03a652><span class="text" data-v-4c03a652><!----> Save <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-4c03a652><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-4c03a652><div class="VPMenu" data-v-4c03a652 data-v-da053c30><div class="items" data-v-da053c30><!--[--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><a class="VPLink link" href="/save/reading/index" data-v-6f715184 data-v-075865b7><!--[-->Reading<!--]--><!----></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-da053c30 data-v-6f715184><a class="VPLink link" href="/academic/vocabulary/index" data-v-6f715184 data-v-075865b7><!--[-->Vocabulary<!--]--><!----></a></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-e99cf6bd data-v-72c0c02a><label title="toggle dark mode" data-v-72c0c02a data-v-cb74fac6><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" aria-checked="false" data-v-cb74fac6 data-v-9f7dbbcf><span class="check" data-v-9f7dbbcf><span class="icon" data-v-9f7dbbcf><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-cb74fac6><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-cb74fac6><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></label></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-e99cf6bd data-v-268ff66d data-v-65dbf981><!--[--><a class="VPSocialLink" href="https://github.com/andatoshiki" aria-label="github" target="_blank" rel="noopener" data-v-65dbf981 data-v-2d45784b><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink" href="https://twitter.com/andatoshiki" aria-label="twitter" target="_blank" rel="noopener" data-v-65dbf981 data-v-2d45784b><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Twitter</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-e99cf6bd data-v-67546bb2 data-v-4c03a652><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-4c03a652><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="icon" data-v-4c03a652><circle cx="12" cy="12" r="2"></circle><circle cx="19" cy="12" r="2"></circle><circle cx="5" cy="12" r="2"></circle></svg></button><div class="menu" data-v-4c03a652><div class="VPMenu" data-v-4c03a652 data-v-da053c30><!----><!--[--><!--[--><!----><div class="group" data-v-67546bb2><div class="item appearance" data-v-67546bb2><p class="label" data-v-67546bb2>Appearance</p><div class="appearance-action" data-v-67546bb2><label title="toggle dark mode" data-v-67546bb2 data-v-cb74fac6><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" aria-checked="false" data-v-cb74fac6 data-v-9f7dbbcf><span class="check" data-v-9f7dbbcf><span class="icon" data-v-9f7dbbcf><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-cb74fac6><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-cb74fac6><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></label></div></div></div><div class="group" data-v-67546bb2><div class="item social-links" data-v-67546bb2><div class="VPSocialLinks social-links-list" data-v-67546bb2 data-v-65dbf981><!--[--><a class="VPSocialLink" href="https://github.com/andatoshiki" aria-label="github" target="_blank" rel="noopener" data-v-65dbf981 data-v-2d45784b><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink" href="https://twitter.com/andatoshiki" aria-label="twitter" target="_blank" rel="noopener" data-v-65dbf981 data-v-2d45784b><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Twitter</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-e99cf6bd data-v-2314de5a><span class="container" data-v-2314de5a><span class="top" data-v-2314de5a></span><span class="middle" data-v-2314de5a></span><span class="bottom" data-v-2314de5a></span></span></button></div></div></div></div><!----></header><div class="VPLocalNav" data-v-83f63849 data-v-fa4746c0><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-fa4746c0><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="menu-icon" data-v-fa4746c0><path d="M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"></path><path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"></path><path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"></path><path d="M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"></path></svg><span class="menu-text" data-v-fa4746c0>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-fa4746c0 data-v-f8dbb575><button data-v-f8dbb575>Return to top</button><!----></div></div><aside class="VPSidebar" data-v-83f63849 data-v-0e47c5d5><div class="curtain" data-v-0e47c5d5></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-0e47c5d5><span class="visually-hidden" id="sidebar-aria-label" data-v-0e47c5d5> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="group" data-v-0e47c5d5><section class="VPSidebarItem level-0 collapsible" data-v-0e47c5d5 data-v-0cc45b6b><div class="item" role="button" tabindex="0" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><h2 class="text" data-v-0cc45b6b>Notes & Issues</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0cc45b6b><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-0cc45b6b><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-0cc45b6b><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/file-naming-convention" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>File Naming Convention</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/rclone-for-r2" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>RClone for R2</p><!--]--><!----></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-0e47c5d5><section class="VPSidebarItem level-0 collapsible has-active" data-v-0e47c5d5 data-v-0cc45b6b><div class="item" role="button" tabindex="0" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><h2 class="text" data-v-0cc45b6b>コードで学ぶAWS入門</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0cc45b6b><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-0cc45b6b><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-0cc45b6b><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/index" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>背景</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/main" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>はじめに!</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/cloud" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>クラウド概論</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/aws-get-started" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>AWS 入門</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/handson-ec2" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Hands-on 1: 初めての EC2 インスタンスを起動する</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/scientific-computing" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>クラウドで行う科学計算・機械学習</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/handson-ec2" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Hands-on 2: AWS でディープラーニングを実践</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/docker-system" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Docker 入門</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/handson-qabot" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Hands-on 3: AWS で自動質問回答ボットを走らせる</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/aws-batch" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Hands-on 4: AWS Batch を使って機械学習のハイパーパラメータサーチを並列化する</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/webserver" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Web サービスの作り方</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/serverless" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Serverless architecture</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/handson-serverless" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Hands-on 5: サーバーレス入門</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link is-active has-active" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/handson-bashoutter" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>Hands-on 6: Bashoutter</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/closing" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>まとめ</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/appendix" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>ppendix: 環境構築</p><!--]--><!----></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0cc45b6b data-v-0cc45b6b><div class="item" data-v-0cc45b6b><div class="indicator" data-v-0cc45b6b></div><a class="VPLink link link" href="/development/aws/acknowledgement" data-v-0cc45b6b data-v-075865b7><!--[--><p class="text" data-v-0cc45b6b>謝辞</p><!--]--><!----></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-83f63849 data-v-bb292142><div class="VPDoc has-sidebar has-aside" data-v-bb292142 data-v-6c4a7022><!--[--><!--]--><div class="container" data-v-6c4a7022><div class="aside" data-v-6c4a7022><div class="aside-curtain" data-v-6c4a7022></div><div class="aside-container" data-v-6c4a7022><div class="aside-content" data-v-6c4a7022><div class="VPDocAside" data-v-6c4a7022 data-v-f77a9b1a><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline" data-v-f77a9b1a data-v-ea95c6a2><div class="content" data-v-ea95c6a2><div class="outline-marker" data-v-ea95c6a2></div><div class="outline-title" data-v-ea95c6a2>TOC</div><nav aria-labelledby="doc-outline-aria-label" data-v-ea95c6a2><span class="visually-hidden" id="doc-outline-aria-label" data-v-ea95c6a2> Table of Contents for current page </span><ul class="root" data-v-ea95c6a2 data-v-74f66e6c><!--[--><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-f77a9b1a></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--[--><!--[--><!--[--><div class="VPDocAsideSponsors"><div class="VPSponsors vp-sponsor aside"><!--[--><section class="vp-sponsor-section"><!----><div class="VPSponsorsGrid vp-sponsor-grid medium"><!--[--><div class="vp-sponsor-grid-item"><a class="vp-sponsor-grid-link" target="_blank" rel="sponsored noopener"><article class="vp-sponsor-grid-box"><h4 class="visually-hidden"></h4><img class="vp-sponsor-grid-image" src="https://jsd.toshiki.dev/gh/andatoshiki/toshiki-notebook@master/assets/logo/sponsor/telegram.png"></article></a></div><!--]--></div></section><!--]--></div></div><!--]--><!--]--><!--]--><!--]--></div></div></div></div><div class="content" data-v-6c4a7022><div class="content-container" data-v-6c4a7022><!--[--><!--]--><!----><main class="main" data-v-6c4a7022><div style="position:relative;" class="vp-doc _development_aws_handson-bashoutter" data-v-6c4a7022><div><h1 id="hands-on-6-bashoutter" tabindex="-1">Hands-on #6: Bashoutter <a class="header-anchor" href="#hands-on-6-bashoutter" aria-label="Permalink to &quot;Hands-on \#6: Bashoutter&quot;"></a></h1><p>さて,最後のハンズオンとなる第六回では,これまで学んできたサーバーレスクラウドの技術を使って,簡単なウェブサービスを作ってみよう. 具体的には,人々が自分の作った俳句を投稿する SNS サービス (<strong>Bashoutter</strong> と名付ける) を作成してみよう. Lambda, DynamoDB, S3 などの技術をすべて盛り込み,シンプルながらもサーバーレスの利点を生かしたスケーラブルな SNS アプリが誕生する. 最終的には, <a href="#handson_05_bashoutter">figure_title</a> のような,ミニマルではあるがとても現代風な SNS サイトが完成する!</p><p><img src="/assets/bashoutter.01c300be.png" alt="ハンズオン#6で作製する SNS アプリケーション &quot;Bashoutter&quot;"></p><h2 id="準備" tabindex="-1">準備 <a class="header-anchor" href="#準備" aria-label="Permalink to &quot;準備&quot;"></a></h2><p>ハンズオンのソースコードは GitHub の <a href="https://github.com/andatoshiki/toshiki-notebooktree/main/handson/bashoutter" target="_blank" rel="noreferrer">handson/bashoutter</a> に置いてある.</p><p>本ハンズオンの実行には,第一回ハンズオンで説明した準備 (<a href="#handson_01_prep">???</a>) が整っていることを前提とする. それ以外に必要な準備はない.</p><p>このハンズオンは,基本的に <a href="https://aws.amazon.com/free/?all-free-tier.sort-by=item.additionalFields.SortRank&amp;all-free-tier.sort-order=asc" target="_blank" rel="noreferrer">AWS の無料枠</a> の範囲内で実行できる.</p><h2 id="アプリケーションの説明" tabindex="-1">アプリケーションの説明 <a class="header-anchor" href="#アプリケーションの説明" aria-label="Permalink to &quot;アプリケーションの説明&quot;"></a></h2><h3 id="api" tabindex="-1">API <a class="header-anchor" href="#api" aria-label="Permalink to &quot;API&quot;"></a></h3><p>今回のアプリケーションでは,人々からの俳句の投稿を受け付けたり,投稿された俳句の一覧を取得する,といった機能を実装したい. この機能を実現するための最小限の設計として, <a href="#tab_handson_05_api">table_title</a> に示すような四つの REST API を今回は実装する. 俳句を投稿する,閲覧する,削除するという基本的なデータ操作を行うための API が完備されている. また, <code>PATCH /haiku/{item_id}</code> は, <code>{item_id}</code> で指定された俳句に”いいね”をするために使用する.</p><table><caption>Bashoutter API</caption><colgroup><col style="width:50%;"><col style="width:50%;"></colgroup><tbody><tr class="odd"><td><p><code>GET /haiku</code></p></td><td><p>俳句の一覧を取得する</p></td></tr><tr class="even"><td><p><code>POST /haiku</code></p></td><td><p>新しい俳句を投稿する</p></td></tr><tr class="odd"><td><p><code>PATCH /haiku/{item_id}</code></p></td><td><p><code>{item_id}</code> で指定された俳句にお気に入り票を一つ入れる</p></td></tr><tr class="even"><td><p><code>DELETE /haiku/{item_id}</code></p></td><td><p><code>{item_id}</code> で指定された俳句を削除する</p></td></tr></tbody></table><p>それぞれの API のパラメータおよび返り値の詳細は,ハンズオンのソースコードの中の <a href="https://github.com/andatoshiki/toshiki-notebookblob/main/handson/bashoutter/specs/swagger.yml" target="_blank" rel="noreferrer">swagger.yml</a> に定義してある.</p><p><strong>Open API Specification</strong> (OAS; 少し前は Swagger Specification とよばれていた) は, REST API のための記述フォーマットである. OAS に従って API の仕様が記述されていると,簡単にドキュメンテーションを生成したり,クライアントアプリケーションを自動生成することができる. <a href="https://github.com/andatoshiki/toshiki-notebookblob/main/handson/bashoutter/specs/swagger.yml" target="_blank" rel="noreferrer">今回用意した API 仕様</a> も, OAS に従って書いてある. 詳しくは <a href="https://swagger.io/docs/specification/about/" target="_blank" rel="noreferrer">Swagger の公式ドキュメンテーション</a> などを参照.</p><h3 id="アプリケーションアーキテクチャ" tabindex="-1">アプリケーションアーキテクチャ <a class="header-anchor" href="#アプリケーションアーキテクチャ" aria-label="Permalink to &quot;アプリケーションアーキテクチャ&quot;"></a></h3><p>このハンズオンで作成するアプリケーションの概要を <a href="#handson_05_architecture">figure_title</a> に示す.</p><p><img src="/assets/handson-05-architecture.dede9426.png" alt="ハンズオン#5で作製するアプリケーションのアーキテクチャ"></p><p>簡単にまとめると,次のような設計である.</p><ul><li><p>クライアントからの API リクエストは, <strong>API Gateway</strong> (後述)にまず送信され, API の URI で指定された Lambda 関数へ転送される.</p></li><li><p>それぞれの API のパス (リソース) ごとに独立した Lambda を用意する.</p></li><li><p>俳句の情報 (作者,本文,投稿日時など) を記録するためのデータベース (DynamoDB) を用意する.</p></li><li><p>各 Lambda 関数には, DynamoDB へのアクセス権を付与する.</p></li><li><p>最後に,ウェブブラウザからコンテンツを表示できるよう, ウェブページの静的コンテンツを配信するための S3 バケットを用意する.クライアントはこの S3 バケットにアクセスすることで HTML/CSS/JS などのコンテンツを取得する.</p></li></ul><p>それでは,プログラムのソースコードを見てみよう (<a href="https://github.com/andatoshiki/toshiki-notebookblob/main/handson/bashoutter/app.py" target="_blank" rel="noreferrer">handson/bashoutter/app.py</a>)</p><div class="language-python vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#93A1A1;font-weight:bold;">class</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">Bashoutter</span><span style="color:#839496;">(</span><span style="color:#6C71C4;">core</span><span style="color:#839496;">.</span><span style="color:#6C71C4;">Stack</span><span style="color:#839496;">):</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#93A1A1;font-weight:bold;">def</span><span style="color:#839496;"> </span><span style="color:#268BD2;">__init__</span><span style="color:#839496;">(self, scope: core.App, name: </span><span style="color:#859900;">str</span><span style="color:#839496;">, </span><span style="color:#859900;">**</span><span style="color:#839496;">kwargs) -&gt; </span><span style="color:#B58900;">None</span><span style="color:#839496;">:</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">super</span><span style="color:#839496;">().</span><span style="color:#268BD2;">__init__</span><span style="color:#839496;">(scope, name, </span><span style="color:#859900;">**</span><span style="color:#839496;">kwargs)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;"># dynamoDB table to store haiku</span></span>
<span class="line"><span style="color:#839496;"> table </span><span style="color:#859900;">=</span><span style="color:#839496;"> ddb.Table(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;Bashoutter-Table&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> partition_key</span><span style="color:#859900;">=</span><span style="color:#839496;">ddb.Attribute(</span></span>
<span class="line"><span style="color:#839496;"> name</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;item_id&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> type</span><span style="color:#859900;">=</span><span style="color:#839496;">ddb.AttributeType.</span><span style="color:#CB4B16;">STRING</span></span>
<span class="line"><span style="color:#839496;"> ),</span></span>
<span class="line"><span style="color:#839496;"> billing_mode</span><span style="color:#859900;">=</span><span style="color:#839496;">ddb.BillingMode.</span><span style="color:#CB4B16;">PAY_PER_REQUEST</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> removal_policy</span><span style="color:#859900;">=</span><span style="color:#839496;">core.RemovalPolicy.</span><span style="color:#CB4B16;">DESTROY</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;"> bucket </span><span style="color:#859900;">=</span><span style="color:#839496;"> s3.Bucket(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;Bashoutter-Bucket&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> website_index_document</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;index.html&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> public_read_access</span><span style="color:#859900;">=</span><span style="color:#B58900;">True</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> removal_policy</span><span style="color:#859900;">=</span><span style="color:#839496;">core.RemovalPolicy.</span><span style="color:#CB4B16;">DESTROY</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> common_params </span><span style="color:#859900;">=</span><span style="color:#839496;"> {</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;runtime&quot;</span><span style="color:#839496;">: _lambda.Runtime.</span><span style="color:#CB4B16;">PYTHON_3_7</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;environment&quot;</span><span style="color:#839496;">: {</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;TABLE_NAME&quot;</span><span style="color:#839496;">: table.table_name</span></span>
<span class="line"><span style="color:#839496;"> }</span></span>
<span class="line"><span style="color:#839496;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;"># define Lambda functions</span></span>
<span class="line"><span style="color:#839496;"> get_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#839496;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;GetHaiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> code</span><span style="color:#859900;">=</span><span style="color:#839496;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#839496;">),</span></span>
<span class="line"><span style="color:#839496;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.get_haiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> memory_size</span><span style="color:#859900;">=</span><span style="color:#D33682;">512</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">**</span><span style="color:#839496;">common_params,</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"><span style="color:#839496;"> post_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#839496;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;PostHaiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> code</span><span style="color:#859900;">=</span><span style="color:#839496;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#839496;">),</span></span>
<span class="line"><span style="color:#839496;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.post_haiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">**</span><span style="color:#839496;">common_params,</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"><span style="color:#839496;"> patch_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#839496;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;PatchHaiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> code</span><span style="color:#859900;">=</span><span style="color:#839496;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#839496;">),</span></span>
<span class="line"><span style="color:#839496;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.patch_haiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">**</span><span style="color:#839496;">common_params,</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"><span style="color:#839496;"> delete_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#839496;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;DeleteHaiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> code</span><span style="color:#859900;">=</span><span style="color:#839496;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#839496;">),</span></span>
<span class="line"><span style="color:#839496;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.delete_haiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">**</span><span style="color:#839496;">common_params,</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;"># grant permissions</span></span>
<span class="line"><span style="color:#839496;"> table.grant_read_data(get_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;"> table.grant_read_write_data(post_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;"> table.grant_read_write_data(patch_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;"> table.grant_read_write_data(delete_haiku_lambda)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#586E75;font-style:italic;"># define API Gateway</span></span>
<span class="line"><span style="color:#839496;"> api </span><span style="color:#859900;">=</span><span style="color:#839496;"> apigw.RestApi(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;BashoutterApi&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> default_cors_preflight_options</span><span style="color:#859900;">=</span><span style="color:#839496;">apigw.CorsOptions(</span></span>
<span class="line"><span style="color:#839496;"> allow_origins</span><span style="color:#859900;">=</span><span style="color:#839496;">apigw.Cors.</span><span style="color:#CB4B16;">ALL_ORIGINS</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> allow_methods</span><span style="color:#859900;">=</span><span style="color:#839496;">apigw.Cors.</span><span style="color:#CB4B16;">ALL_METHODS</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> haiku </span><span style="color:#859900;">=</span><span style="color:#839496;"> api.root.add_resource(</span><span style="color:#2AA198;">&quot;haiku&quot;</span><span style="color:#839496;">)</span></span>
<span class="line"><span style="color:#839496;"> haiku.add_method(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;GET&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> apigw.LambdaIntegration(get_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"><span style="color:#839496;"> haiku.add_method(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;POST&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> apigw.LambdaIntegration(post_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> haiku_item_id </span><span style="color:#859900;">=</span><span style="color:#839496;"> haiku.add_resource(</span><span style="color:#2AA198;">&quot;</span><span style="color:#CB4B16;">{item_id}</span><span style="color:#2AA198;">&quot;</span><span style="color:#839496;">)</span></span>
<span class="line"><span style="color:#839496;"> haiku_item_id.add_method(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;PATCH&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> apigw.LambdaIntegration(patch_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"><span style="color:#839496;"> haiku_item_id.add_method(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;DELETE&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> apigw.LambdaIntegration(delete_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;"> )</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#586E75;font-weight:bold;">class</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">Bashoutter</span><span style="color:#657B83;">(</span><span style="color:#6C71C4;">core</span><span style="color:#657B83;">.</span><span style="color:#6C71C4;">Stack</span><span style="color:#657B83;">):</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#586E75;font-weight:bold;">def</span><span style="color:#657B83;"> </span><span style="color:#268BD2;">__init__</span><span style="color:#657B83;">(self, scope: core.App, name: </span><span style="color:#859900;">str</span><span style="color:#657B83;">, </span><span style="color:#859900;">**</span><span style="color:#657B83;">kwargs) -&gt; </span><span style="color:#B58900;">None</span><span style="color:#657B83;">:</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">super</span><span style="color:#657B83;">().</span><span style="color:#268BD2;">__init__</span><span style="color:#657B83;">(scope, name, </span><span style="color:#859900;">**</span><span style="color:#657B83;">kwargs)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;"># dynamoDB table to store haiku</span></span>
<span class="line"><span style="color:#657B83;"> table </span><span style="color:#859900;">=</span><span style="color:#657B83;"> ddb.Table(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;Bashoutter-Table&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> partition_key</span><span style="color:#859900;">=</span><span style="color:#657B83;">ddb.Attribute(</span></span>
<span class="line"><span style="color:#657B83;"> name</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;item_id&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> type</span><span style="color:#859900;">=</span><span style="color:#657B83;">ddb.AttributeType.</span><span style="color:#CB4B16;">STRING</span></span>
<span class="line"><span style="color:#657B83;"> ),</span></span>
<span class="line"><span style="color:#657B83;"> billing_mode</span><span style="color:#859900;">=</span><span style="color:#657B83;">ddb.BillingMode.</span><span style="color:#CB4B16;">PAY_PER_REQUEST</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> removal_policy</span><span style="color:#859900;">=</span><span style="color:#657B83;">core.RemovalPolicy.</span><span style="color:#CB4B16;">DESTROY</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;"> bucket </span><span style="color:#859900;">=</span><span style="color:#657B83;"> s3.Bucket(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;Bashoutter-Bucket&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> website_index_document</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;index.html&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> public_read_access</span><span style="color:#859900;">=</span><span style="color:#B58900;">True</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> removal_policy</span><span style="color:#859900;">=</span><span style="color:#657B83;">core.RemovalPolicy.</span><span style="color:#CB4B16;">DESTROY</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> common_params </span><span style="color:#859900;">=</span><span style="color:#657B83;"> {</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;runtime&quot;</span><span style="color:#657B83;">: _lambda.Runtime.</span><span style="color:#CB4B16;">PYTHON_3_7</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;environment&quot;</span><span style="color:#657B83;">: {</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;TABLE_NAME&quot;</span><span style="color:#657B83;">: table.table_name</span></span>
<span class="line"><span style="color:#657B83;"> }</span></span>
<span class="line"><span style="color:#657B83;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;"># define Lambda functions</span></span>
<span class="line"><span style="color:#657B83;"> get_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#657B83;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;GetHaiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> code</span><span style="color:#859900;">=</span><span style="color:#657B83;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#657B83;">),</span></span>
<span class="line"><span style="color:#657B83;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.get_haiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> memory_size</span><span style="color:#859900;">=</span><span style="color:#D33682;">512</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">**</span><span style="color:#657B83;">common_params,</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"><span style="color:#657B83;"> post_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#657B83;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;PostHaiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> code</span><span style="color:#859900;">=</span><span style="color:#657B83;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#657B83;">),</span></span>
<span class="line"><span style="color:#657B83;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.post_haiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">**</span><span style="color:#657B83;">common_params,</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"><span style="color:#657B83;"> patch_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#657B83;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;PatchHaiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> code</span><span style="color:#859900;">=</span><span style="color:#657B83;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#657B83;">),</span></span>
<span class="line"><span style="color:#657B83;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.patch_haiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">**</span><span style="color:#657B83;">common_params,</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"><span style="color:#657B83;"> delete_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#657B83;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;DeleteHaiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> code</span><span style="color:#859900;">=</span><span style="color:#657B83;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#657B83;">),</span></span>
<span class="line"><span style="color:#657B83;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.delete_haiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">**</span><span style="color:#657B83;">common_params,</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;"># grant permissions</span></span>
<span class="line"><span style="color:#657B83;"> table.grant_read_data(get_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;"> table.grant_read_write_data(post_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;"> table.grant_read_write_data(patch_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;"> table.grant_read_write_data(delete_haiku_lambda)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#93A1A1;font-style:italic;"># define API Gateway</span></span>
<span class="line"><span style="color:#657B83;"> api </span><span style="color:#859900;">=</span><span style="color:#657B83;"> apigw.RestApi(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;BashoutterApi&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> default_cors_preflight_options</span><span style="color:#859900;">=</span><span style="color:#657B83;">apigw.CorsOptions(</span></span>
<span class="line"><span style="color:#657B83;"> allow_origins</span><span style="color:#859900;">=</span><span style="color:#657B83;">apigw.Cors.</span><span style="color:#CB4B16;">ALL_ORIGINS</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> allow_methods</span><span style="color:#859900;">=</span><span style="color:#657B83;">apigw.Cors.</span><span style="color:#CB4B16;">ALL_METHODS</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> haiku </span><span style="color:#859900;">=</span><span style="color:#657B83;"> api.root.add_resource(</span><span style="color:#2AA198;">&quot;haiku&quot;</span><span style="color:#657B83;">)</span></span>
<span class="line"><span style="color:#657B83;"> haiku.add_method(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;GET&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> apigw.LambdaIntegration(get_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"><span style="color:#657B83;"> haiku.add_method(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;POST&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> apigw.LambdaIntegration(post_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> haiku_item_id </span><span style="color:#859900;">=</span><span style="color:#657B83;"> haiku.add_resource(</span><span style="color:#2AA198;">&quot;</span><span style="color:#CB4B16;">{item_id}</span><span style="color:#2AA198;">&quot;</span><span style="color:#657B83;">)</span></span>
<span class="line"><span style="color:#657B83;"> haiku_item_id.add_method(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;PATCH&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> apigw.LambdaIntegration(patch_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"><span style="color:#657B83;"> haiku_item_id.add_method(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;DELETE&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> apigw.LambdaIntegration(delete_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br><span class="line-number">64</span><br><span class="line-number">65</span><br><span class="line-number">66</span><br><span class="line-number">67</span><br><span class="line-number">68</span><br><span class="line-number">69</span><br><span class="line-number">70</span><br><span class="line-number">71</span><br><span class="line-number">72</span><br><span class="line-number">73</span><br><span class="line-number">74</span><br><span class="line-number">75</span><br><span class="line-number">76</span><br><span class="line-number">77</span><br><span class="line-number">78</span><br><span class="line-number">79</span><br><span class="line-number">80</span><br><span class="line-number">81</span><br><span class="line-number">82</span><br><span class="line-number">83</span><br><span class="line-number">84</span><br><span class="line-number">85</span><br><span class="line-number">86</span><br><span class="line-number">87</span><br><span class="line-number">88</span><br><span class="line-number">89</span><br><span class="line-number">90</span><br><span class="line-number">91</span><br><span class="line-number">92</span><br><span class="line-number">93</span><br><span class="line-number">94</span><br><span class="line-number">95</span><br><span class="line-number">96</span><br></div></div><ul><li><p>ここで,俳句の情報を記録しておくための DynamoDB テーブルを定義している.</p></li><li><p>静的コンテンツを配信するための S3 バケットを用意している.</p></li><li><p>それぞれの API で実行される Lambda 関数を定義している. 関数は Python3.7 で書かれており,コードは <a href="https://github.com/andatoshiki/toshiki-notebookblob/main/handson/bashoutter/api/api.py" target="_blank" rel="noreferrer">handson/bashoutter/api/api.py</a> にある.</p></li><li><p>&lt;3&gt; で定義された Lambda 関数に対し,データベースへの読み書きのアクセス権限を付与している.</p></li><li><p>ここでAPI Gateway により,各 API パスとそこで実行されるべき Lambda 関数を紐付けている.</p></li></ul><p>それぞれの項目について,もう少し詳しく説明しよう.</p><h3 id="public-access-mode-の-s3-バケット" tabindex="-1">Public access mode の S3 バケット <a class="header-anchor" href="#public-access-mode-の-s3-バケット" aria-label="Permalink to &quot;Public access mode の S3 バケット&quot;"></a></h3><p>S3 のバケットを作成しているコードを見てみよう.</p><div class="language-python vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#839496;">bucket </span><span style="color:#859900;">=</span><span style="color:#839496;"> s3.Bucket(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;Bashoutter-Bucket&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> website_index_document</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;index.html&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> public_read_access</span><span style="color:#859900;">=</span><span style="color:#B58900;">True</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> removal_policy</span><span style="color:#859900;">=</span><span style="color:#839496;">core.RemovalPolicy.</span><span style="color:#CB4B16;">DESTROY</span></span>
<span class="line"><span style="color:#839496;">)</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#657B83;">bucket </span><span style="color:#859900;">=</span><span style="color:#657B83;"> s3.Bucket(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;Bashoutter-Bucket&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> website_index_document</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;index.html&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> public_read_access</span><span style="color:#859900;">=</span><span style="color:#B58900;">True</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> removal_policy</span><span style="color:#859900;">=</span><span style="color:#657B83;">core.RemovalPolicy.</span><span style="color:#CB4B16;">DESTROY</span></span>
<span class="line"><span style="color:#657B83;">)</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>ここで注目してほしいのは <code>public_read_access=True</code> の部分だ. 前章で, S3 について説明を行ったときには触れなかったが, S3 には <strong>Public access mode</strong> という機能がある. Public access mode をオンにしておくと,バケットの中のファイルは認証なしで (i.e. インターネット上の誰でも) 閲覧できるようになる. この設定は,一般公開されているウェブサイトの静的なコンテンツを置いておくのに最適であり,多くのサーバーレスによるウェブサービスでこのような設計が行われる. public access mode を設定しておくと, <code>http://XXXX.s3-website-ap-northeast-1.amazonaws.com/</code> のような固有の URL がバケットに対して付与される. そして,クライアントがこの URL にアクセスをすると,バケットの中にある <code>index.html</code> がクライアントに返され,ページがロードされる (どのファイルが返されるかは, <code>website_index_document=&quot;index.html&quot;</code> の部分で設定している.)</p><p>なお,この時点ではバケットは空である. HTML/CSS/JS など静的コンテンツの配置は,デプロイを行った後ほどのステップで行う.</p><p>より本格的なウェブページを運用する際には, public access mode の S3 バケットに, <a href="https://aws.amazon.com/cloudfront/" target="_blank" rel="noreferrer">CloudFront</a> という機能を追加することが一般的である. CloudFront により, <strong>Content Delivery Nework (CDN)</strong> や暗号化された HTTPS 通信を設定することができる. CloudFront についての詳細は <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html" target="_blank" rel="noreferrer">公式ドキュメンテーション &quot;What is Amazon CloudFront?&quot;</a> などを参照いただきたい.</p><p>今回のハンズオンでは説明の簡略化のため CloudFront の設定を行わなかったが,興味のある読者は次のリンクのプログラムが参考になるだろう.</p><ul><li><a href="https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/static-site" target="_blank" rel="noreferrer">https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/static-site</a></li></ul><p>今回の S3 バケットには, AWS によって付与されたランダムな URL がついている. これを. <code>example.com</code> のような自分のドメインでホストしたければ, AWS によって付与された URL を自分のドメインの DNS レコードに追加すればよい.</p><h3 id="api-のハンドラ関数" tabindex="-1">API のハンドラ関数 <a class="header-anchor" href="#api-のハンドラ関数" aria-label="Permalink to &quot;API のハンドラ関数&quot;"></a></h3><p>API リクエストが来たときに,リクエストされた処理を行う関数のことをハンドラ (handler) 関数とよぶ. <code>GET /haiku</code> の API に対してのハンドラ関数を Lambda で定義している部分を見てみよう.</p><div class="language-python vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#839496;">get_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#839496;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;GetHaiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> code</span><span style="color:#859900;">=</span><span style="color:#839496;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#839496;">),</span></span>
<span class="line"><span style="color:#839496;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.get_haiku&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> memory_size</span><span style="color:#859900;">=</span><span style="color:#D33682;">512</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">**</span><span style="color:#839496;">common_params</span></span>
<span class="line"><span style="color:#839496;">)</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#657B83;">get_haiku_lambda </span><span style="color:#859900;">=</span><span style="color:#657B83;"> _lambda.Function(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;GetHaiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> code</span><span style="color:#859900;">=</span><span style="color:#657B83;">_lambda.Code.from_asset(</span><span style="color:#2AA198;">&quot;api&quot;</span><span style="color:#657B83;">),</span></span>
<span class="line"><span style="color:#657B83;"> handler</span><span style="color:#859900;">=</span><span style="color:#2AA198;">&quot;api.get_haiku&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> memory_size</span><span style="color:#859900;">=</span><span style="color:#D33682;">512</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">**</span><span style="color:#657B83;">common_params</span></span>
<span class="line"><span style="color:#657B83;">)</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>簡単なところから見ていくと, <code>memory_size=512</code> の箇所でメモリーの使用量を 512MB に指定している. また, <code>code=_lambda.Code.from_asset(&quot;api&quot;)</code> によって外部のディレクトリ (<code>api/</code>) を参照せよと指定しており, <code>handler=&quot;api.get_haiku&quot;</code> のところで <code>api.py</code> というファイルの <code>get_haiku()</code> という関数をハンドラ関数として実行せよ,と定義している.</p><p>次に,ハンドラ関数として使用されている <code>get_haiku()</code> のコードを見てみよう (<a href="https://github.com/andatoshiki/toshiki-notebookblob/main/handson/bashoutter/api/api.py" target="_blank" rel="noreferrer">handson/bashoutter/api/api.py</a>)</p><div class="language-python vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#839496;">ddb </span><span style="color:#859900;">=</span><span style="color:#839496;"> boto3.resource(</span><span style="color:#2AA198;">&quot;dynamodb&quot;</span><span style="color:#839496;">)</span></span>
<span class="line"><span style="color:#839496;">table </span><span style="color:#859900;">=</span><span style="color:#839496;"> ddb.Table(os.environ[</span><span style="color:#2AA198;">&quot;TABLE_NAME&quot;</span><span style="color:#839496;">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:#93A1A1;font-weight:bold;">def</span><span style="color:#839496;"> </span><span style="color:#268BD2;">get_haiku</span><span style="color:#839496;">(event, context):</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:#2AA198;"> handler for GET /haiku</span></span>
<span class="line"><span style="color:#2AA198;"> &quot;&quot;&quot;</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">try</span><span style="color:#839496;">:</span></span>
<span class="line"><span style="color:#839496;"> response </span><span style="color:#859900;">=</span><span style="color:#839496;"> table.scan()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#839496;"> status_code </span><span style="color:#859900;">=</span><span style="color:#839496;"> </span><span style="color:#D33682;">200</span></span>
<span class="line"><span style="color:#839496;"> resp </span><span style="color:#859900;">=</span><span style="color:#839496;"> response.get(</span><span style="color:#2AA198;">&quot;Items&quot;</span><span style="color:#839496;">)</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">except</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">Exception</span><span style="color:#839496;"> </span><span style="color:#859900;">as</span><span style="color:#839496;"> e:</span></span>
<span class="line"><span style="color:#839496;"> status_code </span><span style="color:#859900;">=</span><span style="color:#839496;"> </span><span style="color:#D33682;">500</span></span>
<span class="line"><span style="color:#839496;"> resp </span><span style="color:#859900;">=</span><span style="color:#839496;"> {</span><span style="color:#2AA198;">&quot;description&quot;</span><span style="color:#839496;">: </span><span style="color:#93A1A1;font-weight:bold;">f</span><span style="color:#2AA198;">&quot;Internal server error. </span><span style="color:#CB4B16;">{</span><span style="color:#859900;">str</span><span style="color:#839496;">(e)</span><span style="color:#CB4B16;">}</span><span style="color:#2AA198;">&quot;</span><span style="color:#839496;">}</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#859900;">return</span><span style="color:#839496;"> {</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;statusCode&quot;</span><span style="color:#839496;">: status_code,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;headers&quot;</span><span style="color:#839496;">: </span><span style="color:#CB4B16;">HEADERS</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;body&quot;</span><span style="color:#839496;">: json.dumps(resp, cls</span><span style="color:#859900;">=</span><span style="color:#839496;">DecimalEncoder)</span></span>
<span class="line"><span style="color:#839496;"> }</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#657B83;">ddb </span><span style="color:#859900;">=</span><span style="color:#657B83;"> boto3.resource(</span><span style="color:#2AA198;">&quot;dynamodb&quot;</span><span style="color:#657B83;">)</span></span>
<span class="line"><span style="color:#657B83;">table </span><span style="color:#859900;">=</span><span style="color:#657B83;"> ddb.Table(os.environ[</span><span style="color:#2AA198;">&quot;TABLE_NAME&quot;</span><span style="color:#657B83;">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:#586E75;font-weight:bold;">def</span><span style="color:#657B83;"> </span><span style="color:#268BD2;">get_haiku</span><span style="color:#657B83;">(event, context):</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:#2AA198;"> handler for GET /haiku</span></span>
<span class="line"><span style="color:#2AA198;"> &quot;&quot;&quot;</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">try</span><span style="color:#657B83;">:</span></span>
<span class="line"><span style="color:#657B83;"> response </span><span style="color:#859900;">=</span><span style="color:#657B83;"> table.scan()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#657B83;"> status_code </span><span style="color:#859900;">=</span><span style="color:#657B83;"> </span><span style="color:#D33682;">200</span></span>
<span class="line"><span style="color:#657B83;"> resp </span><span style="color:#859900;">=</span><span style="color:#657B83;"> response.get(</span><span style="color:#2AA198;">&quot;Items&quot;</span><span style="color:#657B83;">)</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">except</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">Exception</span><span style="color:#657B83;"> </span><span style="color:#859900;">as</span><span style="color:#657B83;"> e:</span></span>
<span class="line"><span style="color:#657B83;"> status_code </span><span style="color:#859900;">=</span><span style="color:#657B83;"> </span><span style="color:#D33682;">500</span></span>
<span class="line"><span style="color:#657B83;"> resp </span><span style="color:#859900;">=</span><span style="color:#657B83;"> {</span><span style="color:#2AA198;">&quot;description&quot;</span><span style="color:#657B83;">: </span><span style="color:#586E75;font-weight:bold;">f</span><span style="color:#2AA198;">&quot;Internal server error. </span><span style="color:#CB4B16;">{</span><span style="color:#859900;">str</span><span style="color:#657B83;">(e)</span><span style="color:#CB4B16;">}</span><span style="color:#2AA198;">&quot;</span><span style="color:#657B83;">}</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#859900;">return</span><span style="color:#657B83;"> {</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;statusCode&quot;</span><span style="color:#657B83;">: status_code,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;headers&quot;</span><span style="color:#657B83;">: </span><span style="color:#CB4B16;">HEADERS</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;body&quot;</span><span style="color:#657B83;">: json.dumps(resp, cls</span><span style="color:#859900;">=</span><span style="color:#657B83;">DecimalEncoder)</span></span>
<span class="line"><span style="color:#657B83;"> }</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br></div></div><p><code>response = table.scan()</code> で,俳句の格納された DynamoDB テーブルから,すべての要素を取り出している. もしなにもエラーが起きなければステータスコード 200 が返され,もしなにかエラーが起こればステータスコード 500 が返されるようになっている.</p><p>上記のような操作を,ほかの API についても繰り返すことで,すべての API のハンドラ関数が定義されている.</p><p><code>GET /haiku</code> のハンドラ関数で, <code>response = table.scan()</code> という部分があるが,実はこれは最善の書き方ではない. DynamoDB の <code>scan()</code> メソッドは,最大で 1MB までのデータしか返さない. データベースのサイズが大きく, 1MB 以上のデータがある場合には,再帰的に <code>scan()</code> メソッドをよぶ必要がある. 詳しくは <a href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.scan" target="_blank" rel="noreferrer">boto3 ドキュメンテーション</a> を参照.</p><h3 id="aws-における権限の管理-iam" tabindex="-1">AWS における権限の管理 (IAM) <a class="header-anchor" href="#aws-における権限の管理-iam" aria-label="Permalink to &quot;AWS における権限の管理 (IAM)&quot;"></a></h3><p>以下の部分のコードに注目してほしい.</p><div class="language-python vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#839496;">table.grant_read_data(get_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;">table.grant_read_write_data(post_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;">table.grant_read_write_data(patch_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;">table.grant_read_write_data(delete_haiku_lambda)</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#657B83;">table.grant_read_data(get_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;">table.grant_read_write_data(post_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;">table.grant_read_write_data(patch_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;">table.grant_read_write_data(delete_haiku_lambda)</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>これまでは説明の簡略化のためにあえて触れてこなかったが, AWS には <a href="https://aws.amazon.com/iam/" target="_blank" rel="noreferrer">IAM (Identity and Access Management)</a> という重要な概念がある. IAM は基本的に,あるリソースがほかのリソースに対してどのような権限をもっているか,を規定するものである. Lambda は,デフォルトの状態ではほかのリソースにアクセスする権限をなにも有していない. したがって, Lambda 関数が DynamoDB のデータを読み書きするためには,それを許可するような IAM が Lambda 関数に付与されていなければならない.今回の S3 バケットには, AWS によって付与されたランダムな URL がついている. これを. <code>example.com</code> のような自分のドメインでホストしたければ, AWS によって付与された URL を自分のドメインの DNS レコードに追加すればよい.</p><p>CDK による <code>dynamodb.Table</code> オブジェクトには <code>grant_read_write_data()</code> という便利なメソッドが備わっており,アクセスを許可したい Lambda 関数を引数としてこのメソッドを呼ぶことで,データベースへの読み書きを許可する IAM を付与することができる. 同様にCDK の <code>s3.Bucket</code> オブジェクトにも <code>grant_read_write()</code> というメソッドが備わっており,これによってバケットへの読み書きを許可することができる. このメソッドは,実は <a href="#sec_aws_batch">???</a> で AWS Batch によるクラスターを構成した際に使用した. 興味のある読者は振り返ってコードを確認してみよう.</p><p>各リソースに付与する IAM は,<strong>必要最低限の権限を与えるにとどめる</strong>というのが基本方針である. これにより,セキュリティを向上させるだけでなく,意図していないプログラムからのデータベースへの読み書きを防止するという点で,バグを未然に防ぐことができる.</p><p>そのような理由により,このコードでは <code>GET</code> のハンドラー関数に対しては <code>grant_read_data()</code> によって, read 権限のみを付与している.</p><h3 id="api-gateway" tabindex="-1">API Gateway <a class="header-anchor" href="#api-gateway" aria-label="Permalink to &quot;API Gateway&quot;"></a></h3><p><a href="https://aws.amazon.com/api-gateway/" target="_blank" rel="noreferrer">API Gateway</a> とは, API の&quot;入り口&quot;としてAPI のリクエストパスに従って Lambda や EC2 などに接続を行うという機能を担う (<a href="#fig:bashoutter_api_gateway">figure_title</a>) Lambda や EC2 によって行われた処理の結果は,再び API Gateway を経由してクライアントに返される. このように,クライアントとバックエンドサーバーの間に立ち, API のリソースパスに応じて接続先を振り分けるようなサーバーを<strong>ルーター</strong>,あるいは<strong>リバースプロキシ</strong>とよんだりする. 従来的には,ルーターにはそれ専用の仮想サーバーが置かれることが一般的であった. しかし, API Gateway はサーバーレスなルーターとして,固定されたサーバーを配置することなく, API のリクエストが来たときのみ起動しAPI のルーティングを実行する. サーバーレスであることの当然の帰結として,アクセスの件数が増大したときにはそれにルーティングの処理能力を自動で増やす機能も備わっている.</p><p><img src="/assets/api_gateway.ebdf9edd.png" alt="API Gateway"></p><p>API Gateway を配置することで,大量 (1 秒間に数千から数万件) の API リクエストに対応することのできるシステムを容易に構築することができる. API Gateway の料金は <a href="#tab_handson_05_apigateway_price">table_title</a> のように設定されている. また,無料利用枠により,月ごとに 100 万件までのリクエストは 0 円で利用できる.</p><table><caption>API Gateway の利用料金設定 (<a href="https://aws.amazon.com/api-gateway/pricing/">参照</a>)</caption><colgroup><col style="width:50%;"><col style="width:50%;"></colgroup><thead><tr class="header"><th>Number of Requests (per month)</th><th>Price (per million)</th></tr></thead><tbody><tr class="odd"><td><p>First 333 million</p></td><td><p>$4.25</p></td></tr><tr class="even"><td><p>Next 667 million</p></td><td><p>$3.53</p></td></tr><tr class="odd"><td><p>Next 19 billion</p></td><td><p>$3.00</p></td></tr><tr class="even"><td><p>Over 20 billion</p></td><td><p>$1.91</p></td></tr></tbody></table><p>ソースコードの該当箇所を見てみよう.</p><div class="language-python vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">python</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;">api </span><span style="color:#859900;">=</span><span style="color:#839496;"> apigw.RestApi(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">self</span><span style="color:#839496;">, </span><span style="color:#2AA198;">&quot;BashoutterApi&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> default_cors_preflight_options</span><span style="color:#859900;">=</span><span style="color:#839496;">apigw.CorsOptions(</span></span>
<span class="line"><span style="color:#839496;"> allow_origins</span><span style="color:#859900;">=</span><span style="color:#839496;">apigw.Cors.</span><span style="color:#CB4B16;">ALL_ORIGINS</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> allow_methods</span><span style="color:#859900;">=</span><span style="color:#839496;">apigw.Cors.</span><span style="color:#CB4B16;">ALL_METHODS</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> )</span></span>
<span class="line"><span style="color:#839496;">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;">haiku </span><span style="color:#859900;">=</span><span style="color:#839496;"> api.root.add_resource(</span><span style="color:#2AA198;">&quot;haiku&quot;</span><span style="color:#839496;">)</span></span>
<span class="line"><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;">haiku.add_method(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;GET&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> apigw.LambdaIntegration(get_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;">)</span></span>
<span class="line"><span style="color:#839496;">haiku.add_method(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;POST&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> apigw.LambdaIntegration(post_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;">haiku_item_id </span><span style="color:#859900;">=</span><span style="color:#839496;"> haiku.add_resource(</span><span style="color:#2AA198;">&quot;</span><span style="color:#CB4B16;">{item_id}</span><span style="color:#2AA198;">&quot;</span><span style="color:#839496;">)</span></span>
<span class="line"><span style="color:#586E75;font-style:italic;">#</span></span>
<span class="line"><span style="color:#839496;">haiku_item_id.add_method(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;PATCH&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> apigw.LambdaIntegration(patch_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;">)</span></span>
<span class="line"><span style="color:#839496;">haiku_item_id.add_method(</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;DELETE&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> apigw.LambdaIntegration(delete_haiku_lambda)</span></span>
<span class="line"><span style="color:#839496;">)</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;">api </span><span style="color:#859900;">=</span><span style="color:#657B83;"> apigw.RestApi(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">self</span><span style="color:#657B83;">, </span><span style="color:#2AA198;">&quot;BashoutterApi&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> default_cors_preflight_options</span><span style="color:#859900;">=</span><span style="color:#657B83;">apigw.CorsOptions(</span></span>
<span class="line"><span style="color:#657B83;"> allow_origins</span><span style="color:#859900;">=</span><span style="color:#657B83;">apigw.Cors.</span><span style="color:#CB4B16;">ALL_ORIGINS</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> allow_methods</span><span style="color:#859900;">=</span><span style="color:#657B83;">apigw.Cors.</span><span style="color:#CB4B16;">ALL_METHODS</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> )</span></span>
<span class="line"><span style="color:#657B83;">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;">haiku </span><span style="color:#859900;">=</span><span style="color:#657B83;"> api.root.add_resource(</span><span style="color:#2AA198;">&quot;haiku&quot;</span><span style="color:#657B83;">)</span></span>
<span class="line"><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;">haiku.add_method(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;GET&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> apigw.LambdaIntegration(get_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;">)</span></span>
<span class="line"><span style="color:#657B83;">haiku.add_method(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;POST&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> apigw.LambdaIntegration(post_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;">haiku_item_id </span><span style="color:#859900;">=</span><span style="color:#657B83;"> haiku.add_resource(</span><span style="color:#2AA198;">&quot;</span><span style="color:#CB4B16;">{item_id}</span><span style="color:#2AA198;">&quot;</span><span style="color:#657B83;">)</span></span>
<span class="line"><span style="color:#93A1A1;font-style:italic;">#</span></span>
<span class="line"><span style="color:#657B83;">haiku_item_id.add_method(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;PATCH&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> apigw.LambdaIntegration(patch_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;">)</span></span>
<span class="line"><span style="color:#657B83;">haiku_item_id.add_method(</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;DELETE&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> apigw.LambdaIntegration(delete_haiku_lambda)</span></span>
<span class="line"><span style="color:#657B83;">)</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br></div></div><ul><li><p>最初に, <code>api = apigw.RestApi()</code> により,空の API Gateway を作成している.</p></li><li><p>次に, <code>api.root.add_resource()</code> のメソッドを呼ぶことで, <code>/haiku</code> という API パスを追加している.</p></li><li><p>続いて, <code>add_method()</code> を呼ぶことで, <code>GET</code>, <code>POST</code> のメソッドを <code>/haiku</code> のパスに定義している.</p></li><li><p>さらに, <code>haiku.add_resource(&quot;{item_id}&quot;)</code> により, <code>/haiku/{item_id}</code> という API パスを追加している.</p></li><li><p>最後に, <code>add_method()</code> を呼ぶことにより, <code>PATCH</code>, <code>DELETE</code> のメソッドを <code>/haiku/{item_id}</code> のパスに定義している.</p></li></ul><p>このように, API Gateway の使い方は非常にシンプルで,逐次的に API パスとそこで実行されるメソッド・Lambda を記述していくだけでよい.</p><p>このプログラムで 新規 API を作成すると, ランダムな URL がその API のエンドポイントとして割り当てられる. これを. <code>api.example.com</code> のような自分のドメインでホストしたければ, AWS によって付与された URL を自分のドメインの DNS レコードに追加すればよい.</p><p>API Gateway で新規 API を作成したとき, <code>default_cors_preflight_options=</code> というパラメータで <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank" rel="noreferrer">Cross Origin Resource Sharing (CORS)</a> の設定を行っている. これは,ブラウザで走る Web アプリケーションと API を接続するときに必要な設定である.</p><h2 id="アプリケーションのデプロイ" tabindex="-1">アプリケーションのデプロイ <a class="header-anchor" href="#アプリケーションのデプロイ" aria-label="Permalink to &quot;アプリケーションのデプロイ&quot;"></a></h2><p>アプリケーションの中身が理解できたところで,早速デプロイを行ってみよう. デプロイの手順は,これまでのハンズオンとほとんど共通である. ここでは,コマンドのみ列挙する (<code>#</code> で始まる行はコメントである) シークレットキーの設定も忘れずに (<a href="#aws_cli_install">???</a>)</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#586E75;font-style:italic;"># プロジェクトのディレクトリに移動</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">cd</span><span style="color:#839496;"> </span><span style="color:#2AA198;">intro-aws/handson/bashoutter</span></span>
<span class="line"></span>
<span class="line"><span style="color:#586E75;font-style:italic;"># venv を作成し,依存ライブラリのインストールを行う</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">python3</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">-m</span><span style="color:#839496;"> </span><span style="color:#2AA198;">venv</span><span style="color:#839496;"> </span><span style="color:#2AA198;">.env</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">source</span><span style="color:#839496;"> </span><span style="color:#2AA198;">.env/bin/activate</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">pip</span><span style="color:#839496;"> </span><span style="color:#2AA198;">install</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">-r</span><span style="color:#839496;"> </span><span style="color:#2AA198;">requirements.txt</span></span>
<span class="line"></span>
<span class="line"><span style="color:#586E75;font-style:italic;"># デプロイを実行</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">cdk</span><span style="color:#839496;"> </span><span style="color:#2AA198;">deploy</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#93A1A1;font-style:italic;"># プロジェクトのディレクトリに移動</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">cd</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">intro-aws/handson/bashoutter</span></span>
<span class="line"></span>
<span class="line"><span style="color:#93A1A1;font-style:italic;"># venv を作成し,依存ライブラリのインストールを行う</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">python3</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">-m</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">venv</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">.env</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">source</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">.env/bin/activate</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">pip</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">install</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">-r</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">requirements.txt</span></span>
<span class="line"></span>
<span class="line"><span style="color:#93A1A1;font-style:italic;"># デプロイを実行</span></span>
<span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">cdk</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">deploy</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>デプロイのコマンドが無事に実行されれば, <a href="#handson_05_cdk_output">figure_title</a> のような出力が得られるはずである. ここで表示されている <code>Bashoutter.BashoutterApiEndpoint = XXXX</code>, <code>Bashoutter.BucketUrl = YYYY</code> の二つ文字列はあとで使うのでメモしておこう.</p><p><img src="/assets/cdk_output.9b6ea7c9.png" alt="CDKデプロイ実行後の出力"></p><p>AWS コンソールにログインして,デプロイされたスタックを確認してみよう. まずは,コンソールから API Gateway のページに行く. すると, <a href="#handson_05_apigw_console_list">figure_title</a> のような画面が表示され,デプロイ済みの API エンドポイントの一覧が確認できる.</p><p><img src="/assets/apigw_console_list.8f31ef30.png" alt="API Gateway コンソール画面 (1)"></p><p>今回デプロイした &quot;BashoutterApi&quot; という名前の API をクリックすることで <a href="#handson_05_apigw_console_detail">figure_title</a> のような画面に遷移し,詳細情報を閲覧できる. <code>GET /haiku</code>, <code>POST /haiku</code> などが定義されていることが確認できる.</p><p>それぞれのメソッドをクリックすると,そのメソッドの詳細情報を確認できる. API Gateway は,前述したルーティングの機能だけでなく,認証機能などを追加することも可能である. このハンズオンではとくにこれらの機能は使用しないが, &quot;Method Request&quot; と書いてある項目などがそれに相当する. 次に, <a href="#handson_05_apigw_console_detail">figure_title</a> で画面右端の赤色で囲った部分に,この API で呼ばれる Lambda 関数が指定されていることに注目しよう. 関数名をクリックと,該当する Lambda のコンソールに遷移し,関数の中身を閲覧することが可能である.</p><p><img src="/assets/apigw_console_detail.a9746ff3.png" alt="API Gateway コンソール画面 (2)"></p><p>次に, S3 のコンソール画面に移ってみよう. <code>bashouter-</code> で始まるランダムな名前のバケットが見つかるはずである (<a href="#handson_05_s3_console">figure_title</a>)</p><p><img src="/assets/s3_console.6207dbfd.png" alt="S3 コンソール画面"></p><p>バケットの名前をクリックすることで,バケットの中身を確認してみよう. <code>index.html</code> のほか, <code>css/</code>, <code>js/</code> などのディレクトリがあるのが確認できるだろう (<a href="#handson_05_s3_contents">figure_title</a>) これらが,ウェブページの&quot;&quot;を定義している静的コンテンツである.</p><p><img src="/assets/s3_contents.f21c5bf9.png" alt="S3 バケットの中身"></p><h2 id="api-リクエストを送信する" tabindex="-1">API リクエストを送信する <a class="header-anchor" href="#api-リクエストを送信する" aria-label="Permalink to &quot;API リクエストを送信する&quot;"></a></h2><p>それでは,デプロイしたアプリケーションに対し,実際に API リクエストを送信してみよう. まずはコマンドラインから API を送信する演習を行おう. S3 に配置した GUI は一旦おいておく.</p><p>ここではコマンドラインから HTTP API リクエストを送信するためのシンプルな HTTP クライアントである <a href="https://httpie.org/" target="_blank" rel="noreferrer">HTTPie</a> を使ってみよう. HTTPie は,スタックをデプロイするときに Python 仮想環境 (venv) を作成したとき,一緒にインストールされている. 念のためインストールがうまくいっているか確認するには,仮想環境を立ち上げたあとコマンドラインに <code>http</code> と打ってみる. ヘルプのメッセージが出力されたら準備 OK である.</p><p>まず,先ほどデプロイを実行したときに得られた API のエンドポイントの URL (<code>Bashoutter.BashoutterApiEndpoint = XXXX</code> で得られた <code>XXXX</code> の文字列) をコマンドラインの変数に設定しておく.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">export</span><span style="color:#839496;"> </span><span style="color:#2AA198;">ENDPOINT_URL=XXXX</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">export</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">ENDPOINT_URL=XXXX</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><p>次に,俳句の一覧を取得するため, <code>GET /haiku</code> の API を送信してみよう.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">http</span><span style="color:#839496;"> </span><span style="color:#2AA198;">GET</span><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku&quot;</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">http</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">GET</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku&quot;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><p>現時点では,まだだれも俳句を投稿していないので,空の配列 (<code>[]</code>) が返ってくる.</p><p>それでは次に, <code>POST /haiku</code> を使って俳句を投稿してみよう.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">http</span><span style="color:#839496;"> </span><span style="color:#2AA198;">POST</span><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku&quot;</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">\</span></span>
<span class="line"><span style="color:#839496;">username=</span><span style="color:#2AA198;">&quot;松尾芭蕉&quot;</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">\</span></span>
<span class="line"><span style="color:#839496;">first=</span><span style="color:#2AA198;">&quot;閑さや&quot;</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">\</span></span>
<span class="line"><span style="color:#839496;">second=</span><span style="color:#2AA198;">&quot;岩にしみ入る&quot;</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">\</span></span>
<span class="line"><span style="color:#839496;">third=</span><span style="color:#2AA198;">&quot;蝉の声&quot;</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">http</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">POST</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku&quot;</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">\</span></span>
<span class="line"><span style="color:#657B83;">username=</span><span style="color:#2AA198;">&quot;松尾芭蕉&quot;</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">\</span></span>
<span class="line"><span style="color:#657B83;">first=</span><span style="color:#2AA198;">&quot;閑さや&quot;</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">\</span></span>
<span class="line"><span style="color:#657B83;">second=</span><span style="color:#2AA198;">&quot;岩にしみ入る&quot;</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">\</span></span>
<span class="line"><span style="color:#657B83;">third=</span><span style="color:#2AA198;">&quot;蝉の声&quot;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>次のような出力が得られるだろう.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">HTTP/1.1</span><span style="color:#839496;"> </span><span style="color:#D33682;">201</span><span style="color:#839496;"> </span><span style="color:#2AA198;">Created</span></span>
<span class="line"><span style="color:#268BD2;">Connection:</span><span style="color:#839496;"> </span><span style="color:#2AA198;">keep-alive</span></span>
<span class="line"><span style="color:#268BD2;">Content-Length:</span><span style="color:#839496;"> </span><span style="color:#D33682;">49</span></span>
<span class="line"><span style="color:#268BD2;">Content-Type:</span><span style="color:#839496;"> </span><span style="color:#2AA198;">application/json</span></span>
<span class="line"><span style="color:#268BD2;">....</span></span>
<span class="line"><span style="color:#839496;">{</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#268BD2;">&quot;description&quot;</span><span style="color:#268BD2;">:</span><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;Successfully added a new haiku&quot;</span></span>
<span class="line"><span style="color:#839496;">}</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">HTTP/1.1</span><span style="color:#657B83;"> </span><span style="color:#D33682;">201</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">Created</span></span>
<span class="line"><span style="color:#268BD2;">Connection:</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">keep-alive</span></span>
<span class="line"><span style="color:#268BD2;">Content-Length:</span><span style="color:#657B83;"> </span><span style="color:#D33682;">49</span></span>
<span class="line"><span style="color:#268BD2;">Content-Type:</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">application/json</span></span>
<span class="line"><span style="color:#268BD2;">....</span></span>
<span class="line"><span style="color:#657B83;">{</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#268BD2;">&quot;description&quot;</span><span style="color:#268BD2;">:</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;Successfully added a new haiku&quot;</span></span>
<span class="line"><span style="color:#657B83;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>新しい俳句を投稿することに成功したようである. 本当に俳句が追加されたか,再び GET リクエストを呼ぶことで確認してみよう.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">http</span><span style="color:#839496;"> </span><span style="color:#2AA198;">GET</span><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#268BD2;">HTTP/1.1</span><span style="color:#839496;"> </span><span style="color:#D33682;">200</span><span style="color:#839496;"> </span><span style="color:#2AA198;">OK</span></span>
<span class="line"><span style="color:#268BD2;">Connection:</span><span style="color:#839496;"> </span><span style="color:#2AA198;">keep-alive</span></span>
<span class="line"><span style="color:#268BD2;">Content-Length:</span><span style="color:#839496;"> </span><span style="color:#D33682;">258</span></span>
<span class="line"><span style="color:#268BD2;">Content-Type:</span><span style="color:#839496;"> </span><span style="color:#2AA198;">application/json</span></span>
<span class="line"><span style="color:#268BD2;">...</span></span>
<span class="line"><span style="color:#839496;">[</span></span>
<span class="line"><span style="color:#839496;"> {</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;created_at&quot;</span><span style="color:#839496;">: </span><span style="color:#2AA198;">&quot;2020-07-06T02:46:04+00:00&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;first&quot;</span><span style="color:#839496;">: </span><span style="color:#2AA198;">&quot;閑さや&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;item_id&quot;</span><span style="color:#839496;">: </span><span style="color:#2AA198;">&quot;7e91c5e4d7ad47909e0ac14c8bbab05b&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;likes&quot;</span><span style="color:#839496;">: </span><span style="color:#D33682;">0.0</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;second&quot;</span><span style="color:#839496;">: </span><span style="color:#2AA198;">&quot;岩にしみ入る&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;third&quot;</span><span style="color:#839496;">: </span><span style="color:#2AA198;">&quot;蝉の声&quot;</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;username&quot;</span><span style="color:#839496;">: </span><span style="color:#2AA198;">&quot;松尾芭蕉&quot;</span></span>
<span class="line"><span style="color:#839496;"> }</span></span>
<span class="line"><span style="color:#839496;">]</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">http</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">GET</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#268BD2;">HTTP/1.1</span><span style="color:#657B83;"> </span><span style="color:#D33682;">200</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">OK</span></span>
<span class="line"><span style="color:#268BD2;">Connection:</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">keep-alive</span></span>
<span class="line"><span style="color:#268BD2;">Content-Length:</span><span style="color:#657B83;"> </span><span style="color:#D33682;">258</span></span>
<span class="line"><span style="color:#268BD2;">Content-Type:</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">application/json</span></span>
<span class="line"><span style="color:#268BD2;">...</span></span>
<span class="line"><span style="color:#657B83;">[</span></span>
<span class="line"><span style="color:#657B83;"> {</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;created_at&quot;</span><span style="color:#657B83;">: </span><span style="color:#2AA198;">&quot;2020-07-06T02:46:04+00:00&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;first&quot;</span><span style="color:#657B83;">: </span><span style="color:#2AA198;">&quot;閑さや&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;item_id&quot;</span><span style="color:#657B83;">: </span><span style="color:#2AA198;">&quot;7e91c5e4d7ad47909e0ac14c8bbab05b&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;likes&quot;</span><span style="color:#657B83;">: </span><span style="color:#D33682;">0.0</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;second&quot;</span><span style="color:#657B83;">: </span><span style="color:#2AA198;">&quot;岩にしみ入る&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;third&quot;</span><span style="color:#657B83;">: </span><span style="color:#2AA198;">&quot;蝉の声&quot;</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;username&quot;</span><span style="color:#657B83;">: </span><span style="color:#2AA198;">&quot;松尾芭蕉&quot;</span></span>
<span class="line"><span style="color:#657B83;"> }</span></span>
<span class="line"><span style="color:#657B83;">]</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br></div></div><p>素晴らしい!</p><p>次に, <code>PATCH /haiku/{item_id}</code> を呼ぶことでこの俳句にいいねを追加してみよう. 一つ前のコマンドで取得した俳句の <code>item_id</code> を,次のコマンドの <code>XXXX</code> に代入した上で実行しよう.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">http</span><span style="color:#839496;"> </span><span style="color:#2AA198;">PATCH</span><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku/XXXX&quot;</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">http</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">PATCH</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku/XXXX&quot;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><p><code>{&quot;description&quot;: &quot;OK&quot;}</code> という出力が得られるはずである. 再び GET リクエストを送ることで,いいね (<code>likes</code>) が 1 増えたことを確認しよう.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">http</span><span style="color:#839496;"> </span><span style="color:#2AA198;">GET</span><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku&quot;</span></span>
<span class="line"><span style="color:#268BD2;">...</span></span>
<span class="line"><span style="color:#839496;">[</span></span>
<span class="line"><span style="color:#839496;"> {</span></span>
<span class="line"><span style="color:#839496;"> ...</span></span>
<span class="line"><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;likes&quot;</span><span style="color:#839496;">: </span><span style="color:#D33682;">1.0</span><span style="color:#839496;">,</span></span>
<span class="line"><span style="color:#839496;"> ...</span></span>
<span class="line"><span style="color:#839496;"> }</span></span>
<span class="line"><span style="color:#839496;">]</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">http</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">GET</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku&quot;</span></span>
<span class="line"><span style="color:#268BD2;">...</span></span>
<span class="line"><span style="color:#657B83;">[</span></span>
<span class="line"><span style="color:#657B83;"> {</span></span>
<span class="line"><span style="color:#657B83;"> ...</span></span>
<span class="line"><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;likes&quot;</span><span style="color:#657B83;">: </span><span style="color:#D33682;">1.0</span><span style="color:#657B83;">,</span></span>
<span class="line"><span style="color:#657B83;"> ...</span></span>
<span class="line"><span style="color:#657B83;"> }</span></span>
<span class="line"><span style="color:#657B83;">]</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>最後に, DELETE リクエストを送ることで俳句をデータベースから削除しよう. <code>XXXX</code><code>item_id</code> の値で置き換えたうえで次のコマンドを実行する.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">http</span><span style="color:#839496;"> </span><span style="color:#2AA198;">DELETE</span><span style="color:#839496;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku/XXXX&quot;</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">http</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">DELETE</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">&quot;</span><span style="color:#859900;">${</span><span style="color:#268BD2;">ENDPOINT_URL</span><span style="color:#859900;">}</span><span style="color:#2AA198;">/haiku/XXXX&quot;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><p>再び GET リクエストを送ることで,返り値が空 (<code>[]</code>) になっていることを確認しよう.</p><p>これで,俳句の投稿・取得・削除そしていいねの追加,といった基本的な API がきちんと動作していることが確認できた.</p><h2 id="大量の-api-リクエストをシミュレートする" tabindex="-1">大量の API リクエストをシミュレートする <a class="header-anchor" href="#大量の-api-リクエストをシミュレートする" aria-label="Permalink to &quot;大量の API リクエストをシミュレートする&quot;"></a></h2><p>さて,前節ではマニュアルで一つずつ俳句を投稿した. 多数のユーザーがいるような SNS では1 秒間に数千件以上の投稿がされている. 今回はサーバーレスアーキテクチャを採用したことで,そのような瞬間的な大量アクセスにも容易に対応できるようなシステムが自動的に構築されている. このポイントを実証するため,ここでは大量の API が送信された状況をシミュレートしてみよう.</p><p><a href="https://github.com/andatoshiki/toshiki-notebookblob/main/handson/bashoutter/client.py" target="_blank" rel="noreferrer">handson/bashoutter/client.py</a> に,大量の API リクエストをシミュレートするためのプログラムが書かれている. このプログラムを使用すると, <code>POST /haiku</code> の API リクエストを指定された回数だけ実行することができる.</p><p>テストとして, API を 300 回実行してみよう. 次のコマンドを実行する.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">python</span><span style="color:#839496;"> </span><span style="color:#2AA198;">client.py</span><span style="color:#839496;"> </span><span style="color:#268BD2;">$ENDPOINT_URL</span><span style="color:#839496;"> </span><span style="color:#2AA198;">post_many</span><span style="color:#839496;"> </span><span style="color:#D33682;">300</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">python</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">client.py</span><span style="color:#657B83;"> </span><span style="color:#268BD2;">$ENDPOINT_URL</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">post_many</span><span style="color:#657B83;"> </span><span style="color:#D33682;">300</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><p>数秒のうちに実行が完了するだろう. これがもし,単一のサーバーからなる API だったとしたら,このような大量のリクエストの処理にはもっと時間がかかっただろう. 最悪の場合には,サーバーダウンにもつながっていたかもしれない. したがって,今回作成したサーバーレスアプリケーションは,とてもシンプルながらも 1 秒間に数百件の処理を行えるような,スケーラブルなクラウドシステムであることがわかる. サーバーレスでクラウドを設計することの利点を垣間見ることができただろうか?</p><p>先述のコマンドにより大量の俳句を投稿するとデータベースに無駄なデータがどんどん溜まってしまう. データベースを完全に空にするには,次のコマンドを使用する.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">python</span><span style="color:#839496;"> </span><span style="color:#2AA198;">client.py</span><span style="color:#839496;"> </span><span style="color:#268BD2;">$ENDPOINT_URL</span><span style="color:#839496;"> </span><span style="color:#2AA198;">clear_database</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">python</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">client.py</span><span style="color:#657B83;"> </span><span style="color:#268BD2;">$ENDPOINT_URL</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">clear_database</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><h2 id="bashoutter-gui-を動かしてみる" tabindex="-1">Bashoutter GUI を動かしてみる <a class="header-anchor" href="#bashoutter-gui-を動かしてみる" aria-label="Permalink to &quot;Bashoutter GUI を動かしてみる&quot;"></a></h2><p>前節ではコマンドラインから API を送信する演習を行った. ウェブアプリケーションでは,これと同じことがウェブブラウザの背後で行われ,ページのコンテンツが表示されている (<a href="#fig:web_server">???</a> 参照) 最後に, API が GUI と統合されるとどうなるのか,見てみよう.</p><p>CDK のコードで, Public access mode の S3 バケットを作成したことを思い出してほしい. 最初のステップとして,ここにウェブサイトのコンテンツをアップロードしよう. ハンズオンのソースコードの中に <code>gui/dist</code> というフォルダが見つかるはずである. ここにはビルド済みのウェブサイトの静的コンテンツ (HTML/CSS/JavaScript) が入っている. AWS CLI のコマンドを使うことでこれらのファイルを S3 にアップロードしよう.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">aws</span><span style="color:#839496;"> </span><span style="color:#2AA198;">s3</span><span style="color:#839496;"> </span><span style="color:#2AA198;">cp</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">--recursive</span><span style="color:#839496;"> </span><span style="color:#2AA198;">./gui/dist</span><span style="color:#839496;"> </span><span style="color:#2AA198;">s3://</span><span style="color:#859900;">&lt;</span><span style="color:#2AA198;">BUCKET_NAM</span><span style="color:#839496;">E</span><span style="color:#859900;">&gt;</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">aws</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">s3</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">cp</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">--recursive</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">./gui/dist</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">s3://</span><span style="color:#859900;">&lt;</span><span style="color:#2AA198;">BUCKET_NAM</span><span style="color:#657B83;">E</span><span style="color:#859900;">&gt;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><p>コマンドを実行する際は, Bashoutter ハンズオンのディレクトリから行うこと (<code>./gui/dist</code> に注目),そして <code>&lt;BUCKET_NAME&gt;</code> にはデプロイした自身のバケットの名前が入る点に注意. 念のためAWS コンソールにログインし,バケットにファイルがアップロードされている点を確認しておこう.</p><p>なお,今回は GUI の説明はとくに行わないが, Bashoutter のウェブサイトは <a href="https://vuejs.org/" target="_blank" rel="noreferrer">Vue.js</a><a href="https://vuetifyjs.com/" target="_blank" rel="noreferrer">Vuetify</a> という UI フレームワークを使って作成した. Vue を使うことで, Single page application (SPA) の技術でウェブサイトの画面がレンダリングされる. ソースコードは <a href="https://github.com/andatoshiki/toshiki-notebooktree/main/handson/bashoutter/gui" target="_blank" rel="noreferrer">handson/bashoutter/gui</a> のディレクトリの中にあるので,興味のある読者は確認してみるとよい.</p><p>アップトードが完了したところで,続いてデプロイを実行したときにコマンドラインの出力を見直してみよう. <code>Bashoutter.BucketUrl=</code> で与えられた URL が見つかるはずである (<a href="#handson_05_cdk_output">figure_title</a>) これは,先述したとおり, Public access mode の S3 バケットの URL である.</p><p>ウェブブラウザを開き,アドレスバーに S3 の URL を入力しへアクセスしてみよう. すると, <a href="#handson_05_bashoutter_2">figure_title</a> のようなページが表示されるはずである.</p><p><img src="/assets/bashoutter_2.6f86b8a8.png" alt="&quot;Bashoutter&quot; の GUI 画面"></p><p>ページが表示されたら,一番上の &quot;API Endpoint URL&quot; と書いてあるテキストボックスに,今回デプロイした <strong>API Gateway の URL を入力</strong>する (今回のアプリケーションではAPI Gateway の URL はランダムに割り当てられるのでこのような GUI の仕様になっている) そうしたら,画面の &quot;REFRESH&quot; と書いてあるボタンを押してみよう. データベースに俳句が登録済みであれば,俳句の一覧が表示されるはずである. 各俳句の左下にあるハートのアイコンをクリックすることで, &quot;like&quot; の票を入れることができる.</p><p>新しい俳句を投稿するには,五七五と投稿者の名前を入力して, &quot;POST&quot; を押す. &quot;POST&quot; を押した後は,再び &quot;REFRESH&quot; ボタンを押すことで最新の俳句のリストをデータベースから取得する.</p><h2 id="アプリケーションの削除" tabindex="-1">アプリケーションの削除 <a class="header-anchor" href="#アプリケーションの削除" aria-label="Permalink to &quot;アプリケーションの削除&quot;"></a></h2><p>これで, Bashoutter プロジェクトが完成した! この SNS は,インターネットを通じて世界のどこからでもアクセスできる状態にある. また, <a href="#simulating_many_apis">大量の API リクエストをシミュレートする</a> で見たように,大量のユーザーの同時アクセスによる負荷がかかっても,柔軟にスケールが行われ遅延なく処理を行うことができる. 極めて簡素ながらも,立派なウェブサービスとしてのスペックは満たしているのである!</p><p>Bashoutter アプリを存分に楽しむことができたら,最後に忘れずにスタックを削除しよう.</p><p>コマンドラインからスタックの削除を実行するには,次のコマンドを使う.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">cdk</span><span style="color:#839496;"> </span><span style="color:#2AA198;">destroy</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">cdk</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">destroy</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><p>CDK のバージョンによっては S3 のバケットが空でないと, <code>cdk destroy</code> がエラーを出力する場合がある. この場合はスタックを削除する前に, S3 バケットの中身をすべて削除しなければならない.</p><p>コンソールから実行するには, S3 コンソールに行き,バケットの中身を開いたうえで,すべてのファイルを選択し, &quot;Actions&quot;&quot;Delete&quot; を実行すればよいい.</p><p>コマンドラインから実行するには, 次のコマンドを使う. &lt;BUCKET NAME&gt; のところは,自分の バケットの名前 (&quot;BashoutterBucketXXXX&quot; というパターンの名前がついているはずである) に置き換えることを忘れずに.</p><div class="language-shell vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki solarized-dark vp-code-dark"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#839496;"> </span><span style="color:#2AA198;">aws</span><span style="color:#839496;"> </span><span style="color:#2AA198;">s3</span><span style="color:#839496;"> </span><span style="color:#2AA198;">rm</span><span style="color:#839496;"> </span><span style="color:#859900;">&lt;</span><span style="color:#2AA198;">BUCKET</span><span style="color:#839496;"> </span><span style="color:#2AA198;">NAM</span><span style="color:#839496;">E</span><span style="color:#859900;">&gt;</span><span style="color:#839496;"> </span><span style="color:#CB4B16;">--recursive</span></span></code></pre><pre class="shiki solarized-light vp-code-light"><code><span class="line"><span style="color:#268BD2;">$</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">aws</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">s3</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">rm</span><span style="color:#657B83;"> </span><span style="color:#859900;">&lt;</span><span style="color:#2AA198;">BUCKET</span><span style="color:#657B83;"> </span><span style="color:#2AA198;">NAM</span><span style="color:#657B83;">E</span><span style="color:#859900;">&gt;</span><span style="color:#657B83;"> </span><span style="color:#CB4B16;">--recursive</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><h2 id="小括" tabindex="-1">小括 <a class="header-anchor" href="#小括" aria-label="Permalink to &quot;小括&quot;"></a></h2><p>ここまでが,本書第三部の内容であった.</p><p>第三部では,クラウドの応用として,一般の人に使ってもらうようなウェブアプリケーション・データベースをどのようにして作るのか,という点に焦点を当てて,説明を行った. その中で,従来的なクラウドシステムの設計と,ここ数年の最新の設計方法であるサーバーレスアーキテクチャについて解説した. <a href="#sec_intro_serverless">???</a> では, AWS でのサーバーレスの実践として, Lambda, S3, DynamoDB のハンズオンを行った. 最後に, <a href="#sec_bashoutter">Hands-on #6: Bashoutter</a> では,これらの技術を統合することで,完全サーバーレスなウェブアプリケーション &quot;Bashoutter&quot; を作成した.</p><p>これらの演習を通じて,世の中のウェブサービスがどのようにしてでき上がっているのか,少し理解が深まっただろうか? また,そのようなウェブアプリケーションを自分が作りたいと思ったとき,今回のハンズオンがその出発点となることができたならば幸いである.</p></div></div></main><footer class="VPDocFooter" data-v-6c4a7022 data-v-b5edbda4><!--[--><!--[--><!--[--><!--[--><!----><!--]--><!--]--><!--]--><!--]--><div class="edit-info" data-v-b5edbda4><div class="edit-link" data-v-b5edbda4><a class="VPLink link edit-link-button" href="https://github.com/andatoshiki/toshiki-notebook/edit/master/docs/development/aws/handson-bashoutter.md" target="_blank" rel="noreferrer" data-v-b5edbda4 data-v-075865b7><!--[--><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="edit-link-icon" aria-label="edit icon" data-v-b5edbda4><path d="M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z"></path><path d="M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z"></path></svg> Edit this page on GitHub<!--]--><!----></a></div><div class="last-updated" data-v-b5edbda4><p class="VPLastUpdated" data-v-b5edbda4 data-v-00fbdbd8>Last updated: <time datetime="2023-09-14T02:57:53.000Z" data-v-00fbdbd8></time></p></div></div><div class="prev-next" data-v-b5edbda4><div class="pager" data-v-b5edbda4><a class="pager-link prev" href="/development/aws/handson-serverless" data-v-b5edbda4><span class="desc" data-v-b5edbda4>Previous page</span><span class="title" data-v-b5edbda4>Hands-on 5: サーバーレス入門</span></a></div><div class="has-prev pager" data-v-b5edbda4><a class="pager-link next" href="/development/aws/closing" data-v-b5edbda4><span class="desc" data-v-b5edbda4>Next page</span><span class="title" data-v-b5edbda4>まとめ</span></a></div></div></footer><!--[--><!--[--><!--[--><div id="comment-container"></div><!--]--><!--]--><!--]--></div></div></div><!--[--><!--]--></div></div><footer class="VPFooter has-sidebar" data-v-83f63849 data-v-02da8605><div class="container" data-v-02da8605><p class="message" data-v-02da8605>Wrote with <i class="heart fa fa-heart fa-xs fa-beat"></i> and <i class="coffee fa fa-coffee fa-xs" aria-hidden="true"></i> by <a href="https://toshiki.dev">Anda Toshiki</a> at <code>root@andatoshiki:/~</code></p><p class="copyright" data-v-02da8605>Copyright © 2023-2023 <a href="https://github.com/andatoshiki">Anda Toshiki</a>, <a href="https://github.com/lolilab">LoliLab</a> and <a href="https://github.com/toshikidev">Toshiki Dev</a> present <br /><span id="siteruntime_span"></span></p></div></footer><!--[--><!--]--></div></div>
<script>__VP_HASH_MAP__ = JSON.parse("{\"academic_physics_index.md\":\"4e2c00b1\",\"save_reading_index.md\":\"9f573ab5\",\"save_reading_outliers_1.md\":\"65ddcd6b\",\"save_reading_outliers_2.md\":\"db6d116c\",\"save_reading_outliers_3.md\":\"8ed04e17\",\"academic_chemistry_index.md\":\"34b4a54d\",\"academic_chemistry_problems_03-02-3.md\":\"4cb2caa0\",\"javascript_notes_1_1-1.md\":\"2716f00e\",\"save_reading_outliers_4.md\":\"83779086\",\"roadmap.md\":\"e210e6dd\",\"javascript_notes_1_1-2.md\":\"d425b1d7\",\"jp_index.md\":\"e1164f22\",\"academic_literature_index.md\":\"eb66b8b4\",\"index.md\":\"42d47c3c\",\"academic_chemistry_notes_12-5.md\":\"3b0320a1\",\"academic_chemistry_problems_02-20.md\":\"e956182a\",\"academic_physics_ipho-formulas-jpn_10.md\":\"17602c36\",\"academic_chemistry_problems_03-02-1.md\":\"79c3a006\",\"academic_chemistry_problems_03-02-2.md\":\"870206d2\",\"academic_physics_ipho-formulas-jpn_2.md\":\"5e56b658\",\"academic_physics_ipho-formulas-jpn_3.md\":\"e21008a9\",\"academic_physics_ipho-formulas-jpn_4.md\":\"d03fff7a\",\"academic_physics_ipho-formulas-jpn_5.md\":\"c9e3f9a2\",\"academic_physics_ipho-formulas-jpn_6.md\":\"1416c9f1\",\"academic_physics_ipho-formulas-jpn_7.md\":\"6fa4fe9c\",\"academic_physics_ipho-formulas-jpn_8.md\":\"da5cfdef\",\"academic_physics_ipho-formulas-jpn_9.md\":\"001f7169\",\"academic_vocabulary_2023_02_2023-02-27.md\":\"97def797\",\"academic_vocabulary_index.md\":\"351cc1c0\",\"application_markdown-it-katex_how-to-use.md\":\"89d1bb29\",\"application_markdown-it-katex_tips.md\":\"4b01d0ce\",\"application_vitepress-plugin-shiki-twoslash_api_annotations.md\":\"de092eb5\",\"application_vitepress-plugin-shiki-twoslash_api_cutting.md\":\"859d6789\",\"application_vitepress-plugin-shiki-twoslash_api_emit.md\":\"a8cbce72\",\"application_vitepress-plugin-shiki-twoslash_api_errors.md\":\"e9b4b09d\",\"application_vitepress-plugin-shiki-twoslash_api_includes.md\":\"5b2a6d92\",\"application_vitepress-plugin-shiki-twoslash_api_logging.md\":\"b479a32c\",\"application_vitepress-plugin-shiki-twoslash_api_multi-file.md\":\"bbf12671\",\"application_vitepress-plugin-shiki-twoslash_api_queries.md\":\"9b81e310\",\"application_vitepress-plugin-shiki-twoslash_api_types.md\":\"add5892a\",\"application_vitepress-plugin-shiki-twoslash_config_flags.md\":\"6564ecf3\",\"application_vitepress-plugin-shiki-twoslash_config_reference.md\":\"2a586147\",\"application_vitepress-plugin-shiki-twoslash_guide_markdown-extensions.md\":\"72fed5d0\",\"application_vitepress-plugin-shiki-twoslash_index.md\":\"c891c5de\",\"development_aws_acknowledgement.md\":\"237f45f4\",\"development_aws_appendix.md\":\"063b1122\",\"development_aws_assignments.md\":\"62e03013\",\"development_aws_author.md\":\"e8dbe5c4\",\"development_aws_aws-batch.md\":\"b7cea1b8\",\"development_aws_aws-get-started.md\":\"c3a06371\",\"development_aws_closing.md\":\"55da37c8\",\"development_aws_cloud.md\":\"8b3c0b39\",\"academic_physics_ipho-formulas-jpn_1.md\":\"ad8073b9\",\"development_aws_docker-system.md\":\"39e5bb4b\",\"development_aws_handson-bashoutter.md\":\"f49bb080\",\"development_aws_handson-ec2.md\":\"19576fe7\",\"development_aws_handson-jupyter.md\":\"2ea60b4a\",\"development_aws_handson-qabot.md\":\"ce75fc63\",\"development_aws_handson-serverless.md\":\"16956f9b\",\"development_aws_index.md\":\"905b8f37\",\"development_aws_license.md\":\"fbe3a17f\",\"development_aws_scientific-computing.md\":\"bdc61c68\",\"development_aws_webserver.md\":\"dbfb1e36\",\"development_file-naming-convention.md\":\"0f97ec7e\",\"development_rclone-for-r2.md\":\"0bca80e1\",\"getting-started.md\":\"4b577f17\",\"academic_literature_writing_methods-of-development.md\":\"8410039a\",\"academic_physics_ipho-formulas-jpn_12.md\":\"53adc115\",\"academic_physics_ipho-formulas-jpn_13.md\":\"ba7aecdd\",\"academic_physics_ipho-formulas-jpn_11.md\":\"c455b0e9\",\"development_aws_serverless.md\":\"63111ba7\",\"application_vitepress-plugin-shiki-twoslash_guide_custom-theme.md\":\"2035427e\",\"development_aws_main.md\":\"b1b2f2c2\",\"application_markdown-it-katex_support-function.md\":\"d43cc018\",\"application_markdown-it-katex_support-table.md\":\"91533621\"}")
__VP_SITE_DATA__ = JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Toshiki's Note\",\"description\":\"Toshiki's web notebook served via Vitepress!\",\"base\":\"/\",\"head\":[],\"appearance\":true,\"themeConfig\":{\"nav\":[{\"text\":\"Development\",\"link\":\"/development/\"},{\"text\":\"Academic\",\"items\":[{\"text\":\"K-12\",\"items\":[{\"text\":\"Chemistry\",\"link\":\"/academic/chemistry/index\",\"activeMatch\":\"/academic/chemistry/\"},{\"text\":\"Discrete Math.\",\"link\":\"/discrete-math/index\",\"activeMatch\":\"/categories/fragments/\"},{\"text\":\"Literature\",\"link\":\"/academic/literature/index\",\"activeMatch\":\"/academic/literature/\"}]},{\"text\":\"Tools\",\"items\":[{\"text\":\"Formulas for IPhO JPN.\",\"link\":\"/academic/physics/ipho-formulas-jpn/1\",\"activeMatch\":\"/academic/physics/ipho-formulas-jpn/\"}]},{\"text\":\"\",\"link\":\"\",\"activeMatch\":\"\"},{\"text\":\"\",\"link\":\"\",\"activeMatch\":\"\"},{\"text\":\"\",\"link\":\"\",\"activeMatch\":\"\"},{\"text\":\"\",\"link\":\"\",\"activeMatch\":\"\"},{\"text\":\"\",\"link\":\"\",\"activeMatch\":\"\"},{\"text\":\"\",\"link\":\"\",\"activeMatch\":\"\"},{\"text\":\"\",\"link\":\"\",\"activeMatch\":\"\"}],\"activeMatch\":\"/academic/\"},{\"text\":\"Application\",\"items\":[{\"text\":\"Personal projects\",\"items\":[{\"text\":\"markdown-it-katex\",\"link\":\"/application/markdown-it-katex/how-to-use\",\"activeMatch\":\"/application/markdown-it-katex/\"},{\"text\":\"vitepress-plugin-shiki-twoslash\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/index\",\"activeMatch\":\"/application/vitepress-plugin-shiki-twoslash/index\"}]}],\"activeMatch\":\"/save/\"},{\"text\":\"Save\",\"items\":[{\"text\":\"Reading\",\"link\":\"/save/reading/index\",\"activeMatch\":\"/save/reading/\"},{\"text\":\"Vocabulary\",\"link\":\"/academic/vocabulary/index\",\"activeMatch\":\"/academic/vocabulary/\"}],\"activeMatch\":\"/save/\"}],\"sidebar\":{\"/development/\":[{\"text\":\"Notes & Issues\",\"collapsed\":false,\"items\":[{\"text\":\"File Naming Convention\",\"link\":\"/development/file-naming-convention\"},{\"text\":\"RClone for R2\",\"link\":\"/development/rclone-for-r2\"}]},{\"text\":\"コードで学ぶAWS入門\",\"collapsed\":false,\"items\":[{\"text\":\"背景\",\"link\":\"/development/aws/index\"},{\"text\":\"はじめに!\",\"link\":\"/development/aws/main\"},{\"text\":\"クラウド概論\",\"link\":\"/development/aws/cloud.md\"},{\"text\":\"AWS 入門\",\"link\":\"/development/aws/aws-get-started\"},{\"text\":\"Hands-on 1: 初めての EC2 インスタンスを起動する\",\"link\":\"/development/aws/handson-ec2.md\"},{\"text\":\"クラウドで行う科学計算・機械学習\",\"link\":\"/development/aws/scientific-computing.md\"},{\"text\":\"Hands-on 2: AWS でディープラーニングを実践\",\"link\":\"/development/aws/handson-ec2.md\"},{\"text\":\"Docker 入門\",\"link\":\"/development/aws/docker-system\"},{\"text\":\"Hands-on 3: AWS で自動質問回答ボットを走らせる\",\"link\":\"/development/aws/handson-qabot\"},{\"text\":\"Hands-on 4: AWS Batch を使って機械学習のハイパーパラメータサーチを並列化する\",\"link\":\"/development/aws/aws-batch\"},{\"text\":\"Web サービスの作り方\",\"link\":\"/development/aws/webserver\"},{\"text\":\"Serverless architecture\",\"link\":\"/development/aws/serverless\"},{\"text\":\"Hands-on 5: サーバーレス入門\",\"link\":\"/development/aws/handson-serverless\"},{\"text\":\"Hands-on 6: Bashoutter\",\"link\":\"/development/aws/handson-bashoutter\"},{\"text\":\"まとめ\",\"link\":\"/development/aws/closing\"},{\"text\":\"ppendix: 環境構築\",\"link\":\"/development/aws/appendix\"},{\"text\":\"謝辞\",\"link\":\"/development/aws/acknowledgement\"}]}],\"/academic/chemistry/\":[{\"text\":\"Textbook\",\"collapsed\":true,\"items\":[{\"text\":\"12-5: Reaction Mechanism\",\"link\":\"/academic/chemistry/notes/12-5\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"}]},{\"text\":\"Kinetics\",\"collapsed\":false,\"items\":[{\"text\":\"Rate determining steps\",\"link\":\"/academic/chemistry/notes/kinetics/rate-determining-step\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"}]},{\"text\":\"Problems & Solutions\",\"collapsed\":true,\"items\":[{\"text\":\"Problem: 02-20\",\"link\":\"/academic/chemistry/problems/02-20\"},{\"text\":\"Problem: 03-02-1\",\"link\":\"/academic/chemistry/problems/03-02-1\"},{\"text\":\"Problem: 03-02-2\",\"link\":\"/academic/chemistry/problems/03-02-2\"},{\"text\":\"Problem: 03-02-3\",\"link\":\"/academic/chemistry/problems/03-02-3\"}]}],\"/academic/physics\":[{\"text\":\"IPhO Formulas: JP Ver.\",\"collapsed\":false,\"items\":[{\"text\":\"1: 数学\",\"link\":\"/academic/physics/ipho-formulas-jpn/1\"},{\"text\":\"2: 一般的な推奨事\",\"link\":\"/academic/physics/ipho-formulas-jpn/2\"},{\"text\":\"3: 運動学\",\"link\":\"/academic/physics/ipho-formulas-jpn/3\"},{\"text\":\"4: 力学\",\"link\":\"/academic/physics/ipho-formulas-jpn/4\"},{\"text\":\"5: 振動と波\",\"link\":\"/academic/physics/ipho-formulas-jpn/5\"},{\"text\":\"6: 幾何光学,測光\",\"link\":\"/academic/physics/ipho-formulas-jpn/6\"},{\"text\":\"7: 波動光学\",\"link\":\"/academic/physics/ipho-formulas-jpn/7\"},{\"text\":\"8: 電気回路\",\"link\":\"/academic/physics/ipho-formulas-jpn/8\"},{\"text\":\"9: 電磁気学\",\"link\":\"/academic/physics/ipho-formulas-jpn/9\"},{\"text\":\"10: 熱力\",\"link\":\"/academic/physics/ipho-formulas-jpn/10\"},{\"text\":\"11: 量子力学\",\"link\":\"/academic/physics/ipho-formulas-jpn/11\"},{\"text\":\"12: Keplerの法則\",\"link\":\"/academic/physics/ipho-formulas-jpn/12\"},{\"text\":\"13: 相対性理論\",\"link\":\"/academic/physics/ipho-formulas-jpn/13\"}]}],\"/academic/vocabulary/\":[{\"text\":\"Vocabulary\",\"collapsed\":true,\"items\":[{\"text\":\"2023-02-27\",\"link\":\"/academic/vocabulary/2023/02/2023-02-27\"}]}],\"/academic/literature/\":[{\"text\":\"Writing Resources\",\"collapsed\":true,\"items\":[{\"text\":\"Patterns of Organization and Methods of Development\",\"link\":\"/academic/literature/writing/methods-of-development\"}]}],\"/javascript/\":[{\"text\":\"1: Basic JavaScript-Value, Variables, and Control Flow\",\"collapsed\":true,\"items\":[{\"text\":\"1-1: Numbers\",\"link\":\"/javascript/notes/1/1-1\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"},{\"text\":\"\",\"link\":\"\"}]}],\"/save/reading/\":[{\"text\":\"Outliers\",\"collapsed\":true,\"items\":[{\"text\":\"Introduction & Chapter 1: The Roseto Mystery\",\"link\":\"/save/reading/outliers/1\"},{\"text\":\"Chapter 2: The 10,000-Hour Rule\",\"link\":\"/save/reading/outliers/2\"},{\"text\":\"Chapter 3: The Trouble with Geniuses, Part 1\",\"link\":\"/save/reading/outliers/3\"},{\"text\":\"Chapter 4: The Trouble with Geniuses, Part 2\",\"link\":\"/save/reading/outliers/4\"}]}],\"/application/markdown-it-katex/\":[{\"text\":\"markdown-it-katex\",\"collapsed\":false,\"items\":[{\"text\":\"1: How to use?\",\"link\":\"/application/markdown-it-katex/how-to-use\"},{\"text\":\"2: KaTeX supported functions\",\"link\":\"/application/markdown-it-katex/support-function\"},{\"text\":\"3: KaTeX support tables\",\"link\":\"/application/markdown-it-katex/support-table\"},{\"text\":\"4: Tips\",\"link\":\"/application/markdown-it-katex/tips\"}]}],\"/application/vitepress-plugin-shiki-twoslash/\":[{\"text\":\"Guide\",\"collapsed\":false,\"items\":[{\"text\":\"Getting Started\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/\"},{\"text\":\"Markdown Extensions\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/guide/markdown-extensions\"},{\"text\":\"Using a Custom Theme\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/guide/custom-theme\"}]},{\"text\":\"Features\",\"collapsed\":false,\"items\":[{\"text\":\"Queries\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/queries\"},{\"text\":\"Errors\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/errors\"},{\"text\":\"Emit\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/emit\"},{\"text\":\"Cutting\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/cutting\"},{\"text\":\"Multi-file\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/multi-file\"},{\"text\":\"@types\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/types\"},{\"text\":\"Meta Annotations\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/annotations\"},{\"text\":\"Logging\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/logging\"},{\"text\":\"Includes\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/api/includes\"}]},{\"text\":\"Config\",\"collapsed\":false,\"items\":[{\"text\":\"Reference\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/config/reference\"},{\"text\":\"Compiler Flags\",\"link\":\"/application/vitepress-plugin-shiki-twoslash/config/flags\"}]}]},\"footer\":{\"copyright\":\"Copyright © 2023-2023 <a href=\\\"https://github.com/andatoshiki\\\">Anda Toshiki</a>, <a href=\\\"https://github.com/lolilab\\\">LoliLab</a> and <a href=\\\"https://github.com/toshikidev\\\">Toshiki Dev</a> present <br /><span id=\\\"siteruntime_span\\\"></span>\",\"message\":\"Wrote with <i class=\\\"heart fa fa-heart fa-xs fa-beat\\\"></i> and <i class=\\\"coffee fa fa-coffee fa-xs\\\" aria-hidden=\\\"true\\\"></i> by <a href=\\\"https://toshiki.dev\\\">Anda Toshiki</a> at <code>root@andatoshiki:/~</code>\"},\"logo\":\"/logos/logo.png\",\"outline\":\"deep\",\"outlineTitle\":\"TOC\",\"outlineBadges\":false,\"lastUpdatedText\":\"Last updated\",\"algolia\":{\"appId\":\"G9IUR45K98\",\"apiKey\":\"8528cc91281d8112b28f508317a96dd3\",\"indexName\":\"toshiki-notebook\"},\"editLink\":{\"pattern\":\"https://github.com/andatoshiki/toshiki-notebook/edit/master/docs/:path\",\"text\":\"Edit this page on GitHub\"},\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/andatoshiki\"},{\"icon\":\"twitter\",\"link\":\"https://twitter.com/andatoshiki\"}]},\"locales\":{\"/\":{\"label\":\"English\",\"lang\":\"en-US\"},\"/jp/\":{\"label\":\"Japanese\",\"title\":\"Vue Test Utils\",\"lang\":\"jp-JP\",\"description\":\"La documentation officielle de Vue Test Utils\"}},\"scrollOffset\":90,\"cleanUrls\":true}")</script>
</body>
</html>