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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
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
730
コミュニティ運営から 中の人に変わって感じたこと
hideokamoto
0
160
Developerが Developer Advocateになった話 / dev-rel-meetup-tokyo-71
hideokamoto
0
400
Jamstack開発者のための App Runner入門
hideokamoto
1
560
WordPressでの webサイト制作2022 / ngk2022s
hideokamoto
0
500
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
250
Other Decks in Programming
See All in Programming
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.2k
すこし踏み込む CancellationToken
htkym
2
1.7k
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
370
Web Framework Benchmark Battle 2026, Any%
a_matsuda
2
1.3k
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.4k
SREの越境 / SRE Collaboration
y0hgi
2
310
Simple Storage Service(S3) is not simple
iwatsukayura
0
280
The Rails Doctrine Decade
koic
2
570
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
140
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
260
AWSに止められる覚悟してますか?
morizo_1984
2
530
Featured
See All Featured
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
530
The Cult of Friendly URLs
andyhume
79
7k
[SF Ruby Conf 2025] Rails X
palkan
3
1.5k
Building an army of robots
kneath
307
47k
How to Talk to Developers About Accessibility
jct
2
580
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
300
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
58k
Chasing Engaging Ingredients in Design
codingconduct
0
360
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
74
43k
Deep Space Network (abreviated)
tonyrice
0
370
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
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/