Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
JavaScript(TypeScript)で メディアサイトを インフラから構築する方法 /...
Search
Hidetaka Okamoto
November 27, 2021
Programming
4.5k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
JavaScript(TypeScript)で メディアサイトを インフラから構築する方法 / jsconf-jp-2021
Hidetaka Okamoto
November 27, 2021
More Decks by Hidetaka Okamoto
See All by Hidetaka Okamoto
OpenAI APIで API Changelogを要約してみた話 / chatgpt-osaka-1
hideokamoto
0
740
コミュニティ運営から 中の人に変わって感じたこと
hideokamoto
0
160
Developerが Developer Advocateになった話 / dev-rel-meetup-tokyo-71
hideokamoto
0
410
Jamstack開発者のための App Runner入門
hideokamoto
1
570
WordPressでの webサイト制作2022 / ngk2022s
hideokamoto
0
510
AWS上でStripeを利用したアプリをより安全にデプロイする方法 /jaws-pankration-2021
hideokamoto
1
260
Shifter Headlessと Headless WordPressの紹介
hideokamoto
0
2.3k
Stripe & Next.js + AWS Amplify で会員 + 定期課金機能 / JP_Stripes20210903
hideokamoto
7
3.3k
後付けで 従量課金プランの 提供を開始した話 / 20210609-jp_stripes
hideokamoto
0
260
Other Decks in Programming
See All in Programming
protojson における文字化けの扱い
kazuki1023
0
240
高橋メソッドでCVE解説シリーズ 〜CVE-2026-112816〜
hakkadaikon
0
240
Seven New Principles of Software Engineering
yegor256
0
470
baseline으로 마이그레이션, 리팩토링 안전하게 맡기기
fornewid
1
970
地方こそサーバーレス — 10年続けた、顔が見えるチームで確かな基盤を / Serverless Matters Most in Regional Areas — 10 Years of Building Reliable Foundations with Close-Knit Teams
seike460
PRO
1
340
느린 네트워크와 저사양 디바이스 사용자를 위한 웹뷰 로딩 시간 개선기
kmkim
1
220
クラウドゲーミングのパフォーマンス最適化に挑む / Cloud Gaming Performance Tuning
vivion
0
330
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
420
SODA - FACT BOOK(JP)
sodainc
1
40k
はじめてのPLATEAU
nokonoko1203
1
640
Modernizing iOS Apps
seyfoyun
0
150
Goコンパイラを作ってGoをもっと理解する / Build a Go compiler to better understand Go
ikura_hamu
0
130
Featured
See All Featured
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.9k
Statistics for Hackers
jakevdp
799
230k
The SEO Collaboration Effect
kristinabergwall1
1
620
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
We Have a Design System, Now What?
morganepeng
55
8.4k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
121
130k
Facilitating Awesome Meetings
lara
57
7.2k
What's in a price? How to price your products and services
michaelherold
247
13k
Become a Pro
speakerdeck
PRO
31
6.4k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
20k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.6k
Embracing the Ebb and Flow
colly
88
5.2k
Transcript
JavaScript(TypeScript)Ͱ ϝσΟΞαΠτΛ Πϯϑϥ͔Βߏங͢Δํ๏ JSConf JP 2021 2021/11/27 Hidetaka Okamoto
Agenda • WebαΠτߏஙʹ͓͚ΔΠϯϑϥͷଟ༷Խ • IaaS / FaaSͱIaCΛར༻ͨ͠JSΦϯϦʔͷΠϯϑϥఆٛ • AWSʹ͓͚ΔJSͷΈͰͷߏஙύλʔϯ3छྨ •
AWSͰfaust.js(Next.js)ͰWordPressΛར༻͢Δ߹ͷҙ
About me • Ԭຊलߴ(Okamoto Hidetaka) • JS(TS) developer • React
/ NestJS / AWS CDK • AWS / Stripe / Alexa / Shifter • དྷि͔Β࠶ͼձࣾһ
Talk about… • My Blog ( https://wp-kyoto.net ) • Backend:
WordPress • Frontend: Next.js (with Ionic) • Infrastructure: AWS (AWS CDK) • Misc: Stripe / AWS Amplify / Algolia
Agenda • WebαΠτߏஙʹ͓͚ΔΠϯϑϥͷଟ༷Խ • IaaS / FaaSͱIaCΛར༻ͨ͠JSΦϯϦʔͷΠϯϑϥఆٛ • AWSʹ͓͚ΔJSͷΈͰͷߏஙύλʔϯ3छྨ •
AWSͰfaust.js(Next.js)ͰWordPressΛར༻͢Δ߹ͷҙ
WebαΠτߏஙͰͷΠϯϑϥͷଟ༷Խ(Ұྫ) • αʔόʔωοτϫʔΫΛཧ͔Β༻ҙɾηοτΞοϓ • αʔόʔΛआΓͯɺͦͷதʹLAMP / MEAN stackͳͲΛߏ (VPS) •
ίϯςφΛར༻ͨ͠ɺImmutableͳڥΛڞ༗ɾσϓϩΠ (GAE/EKS) • ϑϧϚωʔδυͳSaaSΛͬͯɺΞϓϦͷΈσϓϩΠ (Netlify / Vercel) • ΞϓϦϚωʔδυܕͷSaaSͰɺαΠτӡӦʹूத (WP.com/STUDIO)
Πϯϑϥͷίʔυཧ “Infrastructure as Code (IaC)” • Chef / Ansible /
terraform / Serverless Framework / Docker / etc… • ʮ͋Δ͖ߏʯΛίʔυͰఆٛ͠ɺVCSͰཧ͢Δ • ʮͦͷίʔυ͕͋Εɺ୭Ͱಉ͡ڥ͕࡞ΕΔʯ • ʮԶͷڥͰಈ͍͚ͨͲʁʯͷղফ • αʔόʔ૿ڧೖΕସ͑࣌ͷޮԽͱ࡞ۀϛεͷ༧
IaCͷఆ͕ٛ JavaScriptͰॻ͚Εɺ Ͱ͖Δ͜ͱ͕૿͑Δɾɾɾʁ
Agenda • WebαΠτߏஙʹ͓͚ΔΠϯϑϥͷଟ༷Խ • IaaS / FaaSͱIaCΛར༻ͨ͠JSΦϯϦʔͷΠϯϑϥఆٛ • AWSʹ͓͚ΔJSͷΈͰͷߏஙύλʔϯ3छྨ •
AWSͰfaust.js(Next.js)ͰWordPressΛར༻͢Δ߹ͷҙ
JSͰΠϯϑϥఆ͕ٛՄೳͳπʔϧ(Ұ෦) • Serverless Framework (AWSͷΈ) • AWS CDK • Adapt
Serverless Framework (JS Objectܕ) import type { AWS } from
'@serverless/typescript'; const serverlessConfiguration: AWS = { service: 'aws-nodejs-typescript', frameworkVersion: '*', provider: { name: 'aws', runtime: 'nodejs12.x', }, functions: { hello: { handler: 'handler.hello', events: [ { http: { method: 'get', path: 'hello', } } ] } } } • AWSͷΈରԠ(2021/11) • ܕใ: @types/serverless • AWS Lambda + API GW
AWS CDK (Classܕ) import * as core from "@aws-cdk/core"; import
* as apigateway from "@aws-cdk/aws-apigateway"; import * as lambda from "@aws-cdk/aws-lambda"; export class WidgetService extends core.Construct { constructor(scope: core.Construct, id: string) { super(scope, id); const handler = new lambda.Function(this, "WidgetHandler", { runtime: lambda.Runtime.NODEJS_14_X, // So we can use async in widget.js code: lambda.Code.fromAsset("resources"), handler: "main", }); bucket.grantReadWrite(handler); // was: handler.role); const api = new apigateway.RestApi(this, "widgets-api", { restApiName: "Widget Service", description: "This service serves widgets." }); const getWidgetsIntegration = new apigateway.LambdaIntegration(handler, { requestTemplates: { "application/json": '{ "statusCode": "200" }' } }); api.root.addMethod("GET", getWidgetsIntegration); // GET / } } • CloudFormationͷϥούʔ • AWSϦιʔεఆٛΛ TypeScriptͰॻ͚Δ • CloudFormation YAMLͷ มͰग़Մೳ
Adapt (JSXܕ) import Adapt, { Group, handle } from "@adpt/core";
import { UrlRouter } from "@adpt/cloud/http"; import { NodeService, ReactApp } from "@adpt/cloud/nodejs"; import { Postgres } from "@adpt/cloud/postgres"; import { k8sProdStyle, k8sTestStyle, laptopStyle } from "./ styles"; function App() { const pg = handle(); const app = handle(); const api = handle(); return <Group> <UrlRouter port={8080} routes={[ { path: "/api/", endpoint: api }, { path: "/", endpoint: app } ]} /> <ReactApp handle={app} srcDir="../frontend" /> <NodeService handle={api} srcDir="../backend" connectTo={pg} /> <Postgres handle={pg} /> </Group>; } • React JSXͰߏఆٛ • AWSҎ֎Ͱ͑Δ • v0.xͷͨΊɺ prodಋೖ৻ॏʹ
JSΛͬͨΠϯϑϥఆٛ • Class / Object / JSXͳͲɺ༷ʑͳه๏͕Մೳ • TypeScriptͷܕఆٛΛ׆༻࣮͕ͯ͠جຊܗ •
AWS / GCPͳͲͷαʔϏεಠࣗͷઃఆఆٛʹTSͷྗ͕͑Δ • ʢ؍ଌൣғͰʣެࣜͰπʔϧΛϦϦʔε͍ͯ͠ΔAWS͕ڧΊ
Agenda • WebαΠτߏஙʹ͓͚ΔΠϯϑϥͷଟ༷Խ • IaaS / FaaSͱIaCΛར༻ͨ͠JSΦϯϦʔͷΠϯϑϥఆٛ • AWSʹ͓͚ΔJSͷΈͰͷߏஙύλʔϯ3छྨ •
AWSͰfaust.js(Next.js)ͰWordPressΛར༻͢Δ߹ͷҙ
AWS CDK import * as core from "@aws-cdk/core"; import *
as apigateway from "@aws-cdk/aws-apigateway"; import * as lambda from "@aws-cdk/aws-lambda"; export class WidgetService extends core.Construct { constructor(scope: core.Construct, id: string) { super(scope, id); const handler = new lambda.Function(this, "WidgetHandler", { runtime: lambda.Runtime.NODEJS_14_X, // So we can use async in widget.js code: lambda.Code.fromAsset("resources"), handler: "main", }); bucket.grantReadWrite(handler); // was: handler.role); const api = new apigateway.RestApi(this, "widgets-api", { restApiName: "Widget Service", description: "This service serves widgets." }); const getWidgetsIntegration = new apigateway.LambdaIntegration(handler, { requestTemplates: { "application/json": '{ "statusCode": "200" }' } }); api.root.addMethod("GET", getWidgetsIntegration); // GET / } }
AWS CDKΛ༻͍ͨߏஙํ๏ • CloudFormationΛTypeScriptͰهड़͢Δ • Construct LibraryΛར༻͢Δ • AWS AmplifyΛOverwrite͢ΔʢNEW
!ʣ
CloudFormationΛ TypeScriptͰ هड़͢Δ const bucket = new s3.CfnBucket(this, "MyBucket", {
bucketName: "MyBucket", corsConfiguration: { corsRules: [{ allowedOrigins: ["*"], allowedMethods: ["GET"] }] } }); const cfnDistribution = new cloudfront.CfnDistribution(this, 'MyCfnDistribution', { distributionConfig: { enabled: false, origins: [{ domainName: 'domainName', id: 'id', // the properties below are optional connectionAttempts: 123, connectionTimeout: 123, originCustomHeaders: [{ headerName: 'headerName', headerValue: 'headerValue', }], originPath: 'originPath', originShield: { enabled: false, originShieldRegion: 'originShieldRegion', }, s3OriginConfig: { originAccessIdentity: 'originAccessIdentity', }, }], … • AWSϦιʔεΛ 1ͭ1ͭఆٛ • 👍 ৄࡉͳઃఆ͕Մೳ • 😖 AWSͷ͕ࣝଟ͘ඞཁ
AWS CDKͷ Construct Library Λར༻͢Δ import * as core from
"@aws-cdk/core"; import * as apigateway from "@aws-cdk/aws-apigateway"; import * as lambda from "@aws-cdk/aws-lambda"; import * as s3 from "@aws-cdk/aws-s3"; export class WidgetService extends core.Construct { constructor(scope: core.Construct, id: string) { super(scope, id); const bucket = new s3.Bucket(this, "WidgetStore"); const handler = new lambda.Function(this, "WidgetHandler", { runtime: lambda.Runtime.NODEJS_14_X, // So we can use async in widget.js code: lambda.Code.fromAsset("resources"), handler: "widgets.main", environment: { BUCKET: bucket.bucketName } }); bucket.grantReadWrite(handler); // was: handler.role); const api = new apigateway.RestApi(this, "widgets-api", { restApiName: "Widget Service", description: "This service serves widgets." }); const getWidgetsIntegration = new apigateway.LambdaIntegration(handler, { requestTemplates: { "application/json": '{ "statusCode": "200" }' } }); api.root.addMethod("GET", getWidgetsIntegration); // GET / } } • CFNϦιʔεΛ நԽͨ͠ϥΠϒϥϦ܈ • 👍 ͋ΔఔఆٛࡁΈͷ ɹߏ͕͑Δ • 😖 ઃఆ͕not for meͷ߹ ɹ݁ہίʔυΛॻ͘͜ͱʹ
@aws-cdk/aws- lambda-nodejs ͕ศར new lambda.NodejsFunction(this, 'my-handler', { bundling: { commandHooks:
{ beforeBundling(inputDir: string, outputDir: string): string[] { return [ `echo hello > ${inputDir}/a.txt`, ]; }, afterBundling(inputDir: string, outputDir: string): string[] { return [`cp ${inputDir}/b.txt ${outputDir}/txt`]; }, beforeInstall() { return []; }, }, minify: true, // minify code, defaults to false sourceMap: true, // include source map, defaults to false target: 'es2020', loader: { // Use the 'dataurl' loader for '.png' files '.png': 'dataurl', }, define: { // Replace strings during build time 'process.env.API_KEY': JSON.stringify('xxx-xxxx-xxx'), }, logLevel: lambda.LogLevel.SILENT, // defaults to LogLevel.WARNING keepNames: true, // defaults to false tsconfig: 'custom-tsconfig.json', // use custom-tsconfig.json instead of default, … • TypeScriptͷίʔυΛ esbuildͰίϯύΠϧͯ͠ AWS LambdaʹσϓϩΠ • Lambdaͷ࣮ͱߏ ྆ํ·ͱΊͯTSͰཧͰ͖Δ
Next.jsΛ͏߹ Serverless Next.js https://github.com/serverless-nextjs/serverless-next.js
serverless-nextjs for AWS CDK // Πϯϑϥఆٛ import { NextJSLambdaEdge }
from "@sls-next/cdk-construct"; const app = new NextJSLambdaEdge(this, "NextJsApp", { serverlessBuildOutDir: "./build", domain: { domainNames: ["wp-kyoto.net"], certificate: Certificate.fromCertificateArn( this, “WPKYOTOACM", "arn:aws:acm:us-east-1:xxxxxx" ), }, }); ==================================== // Next.jsͷϏϧυઃఆ import * as cdk from "@aws-cdk/core"; import { Builder } from "@sls-next/lambda-at-edge"; const builder = new Builder(".", "./build", { args: ["build"] }); builder .build().then(() => { const app = new cdk.App(); new MyStack(app, `MyStack`); }) • NextJSLambdaEdgeͰ Next.jsΛCDNΤοδʹσϓϩΠ • CDKͳͷͰΧελϚΠζ༰қ • Next.jsͷ࣮ʹखΛग़͞ͳ͍ͷ Ͱɺ؆୯ʹࣙΊΕΔ
CDKͰͷ ΧελϚΠζྫ const app = new NextJSLambdaEdge(…); // Ωϟογϡઃఆ app.nextLambdaCachePolicy
= new CachePolicy(this, "NextLambdaCache", { queryStringBehavior: CacheQueryStringBehavior.all(), headerBehavior: CacheHeaderBehavior.allowList( "Authorization", “Origin" ), cookieBehavior: { behavior: "all", }, defaultTtl: Duration.seconds(0), maxTtl: Duration.days(365), minTtl: Duration.seconds(0), enableAcceptEncodingBrotli: true, enableAcceptEncodingGzip: true, }); // AWSϦιʔεͷΞΫηεݖ const targets = [app.defaultNextLambda, app.defaultNextLambda]; targets.forEach((target) => { target.addToRolePolicy( new PolicyStatement({ resources: ["*"], actions: ["ssm:GetParameter"], }) ); }); • ΫϥεͷpropsΛ ্ॻ͖ͯ͠ߋ৽ • CDNઃఆΛҰ෦্ॻ͖ • AWSͷΞΫηεݖݶΛ ֦ு
σΟϨΫτϦߏ !"" README.md !"" next-env.d.ts !"" next.config.js !"" tsconfig.json !""
package.json !"" pages !"" public !"" styles !"" components !"" hooks !"" tests # %"" cdk !"" tsconfig.cdk.json !"" cdk # !"" bin # %"" serverless-nextjs-cdk-example-stack.ts !"" cdk.json !"" jest.cdk.config.js %"" yarn.lock • Next.jsجຊߏʹ cdkσΟϨΫτϦΛՃ • CDKελοΫͷTS ผ్tsconfigΛ࡞
GitHubʹࢼͤΔ αϯϓϧPJ͋Γ·͢ https://bit.ly/3cVCXCt ͳ͔ͬͨͷͰPull RequestΛग़ͯ͠ࡌͤͯΒͬͨ
Serverless Next.jsΛ͏߹ͷҙ • Lambda@edge্Ͱಈ࡞͍ͯ͠Δ͜ͱΛΕͳ͍ • ϑΝΠϧαΠζ࣮ߦ࣌ؒͷ੍ݶ͕͋Δ • FileSystemΛ͏ܥͷػೳࣄނΓ͍͢ • ϩά͕ʮΞΫηε͞ΕͨϦʔδϣϯͷCloudWatch
Logʯʹࢄ • ʮ࠷৽ͷػೳʯ͑ΔΑ͏ʹͳΔ·ͰλΠϜϥά͕ग़Δ
AWS AmplifyΛ Overwrite͢Δ import { AmplifyS3ResourceTemplate } from '@aws-amplify/cli-extensibility-helper'; export
function override( resources: AmplifyS3ResourceTemplate ) { resources.s3Bucket.versioningConfiguration = { status: "Enabled" } } • amplify override <resource> • 👍 ΧελϜ͍ͨ͠ॴ͚ͩ ɹίʔυͰมߋͰ͖Δ • 😖 શϦιʔεʹ ɹ·ͩະରԠ
AWSͰJSΛͬͯΠϯϑϥΛఆٛ͢Δ • REST API։ൃத৺ -> Serverless FW or AWS CDK
(Nodejs Construct) • ࡉ͔͘ઃఆΛॻ͖͍ͨΘ͚Ͱແ͍ -> AWS Amplify (+ amplify overrideͰAWS CDKʣ • Next.jsΛ྿Ձ͔ͭServerlessʹӡ༻͍ͨ͠ -> Serverless Next.js • ΄΅CloudFormationΛॻ͘ͷʹ͍ۙ -> AWS CDK
Agenda • WebαΠτߏஙʹ͓͚ΔΠϯϑϥͷଟ༷Խ • IaaS / FaaSͱIaCΛར༻ͨ͠JSΦϯϦʔͷΠϯϑϥఆٛ • AWSʹ͓͚ΔJSͷΈͰͷߏஙύλʔϯ3छྨ •
AWSͰfaust.js(Next.js)ͰWordPressΛར༻͢Δ߹ͷ ҙ
https://faustjs.org/
WordPress / GraphQL / Next.js -> Faust.js • WP EngineʢWPϗεςΟϯάձࣾʣ͕ϦϦʔε
• WordPressͷϑϩϯτΤϯυΛNext.jsʹ͢ΔͨΊͷFW (ݱঢ়) • WordPressཧը໘࿈ܞ • ϓϨϏϡʔػೳ • GraphQLεΩʔϚAuth༻React Hookͷఏڙ etc…
Setup npx create-next-app \ -e https://github.com/wpengine/faustjs/tree/canary \ --example-path examples/next/getting-started \
--use-npm \ my-app cd my-app
import { GetServerSidePropsContext } from 'next'; import { getNextServerSideProps }
from '@faustjs/next'; import { client } from 'client'; export default function MyPage() { const { usePosts } = client; return ( <> <h2>Recent Posts</h2> {usePosts()?.nodes.map((post) => ( <li key={post.id}>{post.title()}</li> ))} </> ); } export async function getServerSideProps(context: GetServerSidePropsContext) { return getNextServerSideProps(context, { Page: MyPage, client, }); } WPͷσʔλऔಘ • Faust.jsఏڙͷ HookؔΛ͏ • GQtyͰGraphQLͷ APIݺͼग़͠ • WPଆͷ༷Λҙࣝͤͣ͑Δ
AWSͰࢼ͢ࡍͷҙ • Node.js v14Ҏ্͕ඞਢ • Lambda@edgeAppRunnerNode v12ʢ2021/11࣌ʣ • Serverless Next.js͕ɺݱঢ়͑ͳ͍ʢAWSͷରԠͪʣ
• v12ͷEOL͕2022/4͝ΖͳͷͰɺͦͷ͏ͪv14ରԠ͢Δʢͣʣ • ͯͳ͍ਓɺFargateEC2ɾebΛ
JavaScript(TS)ͰϝσΟΞαΠτΛ࡞Δ • αʔϏεɾπʔϧͷਐԽʹΑΓɺ JS͚ͩͰ࣮ɾఆٛͰ͖ΔྖҬ͕૿͑ͨ • Construct / 3rd party libraryʹΑΓɺ
ʮதΛҙࣝͤͣʹಈ͔ͤΔڥઃఆʯೖख͕༰қʹ • ʮ0͔1͔ʯͰͳ͍ɻ͕ɺ ʮ͚ࣗͨͪͩͰͰ͖Δํ๏ʯΛ͍ͬͯΔ͜ͱॏཁ
Thanks • SNS: Twitter->@hide__dev GitHub->hideokamoto • Resources • AWS CDK:
https://aws.amazon.com/jp/cdk/ • Serverless Framework: https://www.serverless.com/framework • Serverless Next.js: https://serverless-nextjs.com/ • Faust.js: https://faustjs.org/