Nessa postagem vamos aprender como fazer o deploy de uma aplicação Remix no serverless. Caso só queria ver o código, acesse esse link.
Ao criar uma nova aplicação Remix temos a possibilidade de escolher onde iremos subir nossa aplicação como Remix App Server, Express Server, Architect (AWS Lambda), Fly.io, Netlify, Vercel e Cloudflare Pages. Alguns desses não dão tanta margem pra escolha como Netlify e Vercel e outros requerem uma infraestrutura um pouco mais robusta para rodar como o Express Server. Pessoalmente eu prefiro ter controle total da minha aplicação ao invés de depender do dashboard do Vercel ou Netlify e das restrições que essas plataformas impõe, e mesmo que o Architect seja extremamente parecido com o que vamos aprender aqui, ele também tem suas próprias restrições e uma documentação não tão boa para iniciantes.
Por isso, ao conhecer o framework Remix eu fiquei curioso para saber se funcionaria em um ambiente serverless como o AWS Lambda, tendo total controle no deploy e sobre como a aplicação funciona. Procurando soluções já prontas me deparei com o repositório shamsup/remix-starter-serverless, que possui um README muito bem detalhado do processo e é basicamente isso que vamos resumir nessa postagem.
Para iniciar, podemos criar uma nova aplicação Remix usando o template do Architect (AWS Lambda), que possui uma configuração similar ao que vamos usar aqui.
npx create-remix@latest
? Where would you like to create your app? remix-serverless
? What type of app do you want to create? Just the basics
? Where do you want to deploy? Architect (AWS Lambda)
? TypeScript or JavaScript? TypeScript
? Do you want me to run `npm install`? Yes
💿 That's it! `cd` into "/Users/rafael/Developer/remix-serverless" and check the README for development and deploy instructions!
Logo de cara podemos apagar alguns arquivos que não vamos utilizar como server.js
, app.arc
e server/config.arc
. Também podemos apagar os scripts dev:arc
e start
do nosso package.json
.
"scripts": {
"build": "remix build",
"dev:remix": "remix watch",
"dev:arc": "cross-env NODE_ENV=development arc sandbox",
"dev": "remix build && run-p \"dev:*\"",
"start": "cross-env NODE_ENV=production arc sandbox"
}
Agora vamos instalar as dependências do serverless pra nossa configuração.
npm install serverless esbuild serverless-esbuild serverless-s3-sync -D
No arquivo remix.config.js
, vamos apagar o campo server
e adicionar um novo chamado serverBuildDirectory
.
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
serverBuildTarget: 'arc',
ignoredRouteFiles: ['**/.*'],
server: 'server.js',
serverBuildDirectory: 'server/build',
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// serverBuildPath: "server/index.js",
// publicPath: "/_static/build/",
}
Com as dependências instaladas e o arquivo de configuração atualizado, podemos criar dois novos arquivo chamados serverless.yml
onde ficará a configuração da nossa aplicação e server/remix.ts
onde ficará o nosso Lambda handler do Remix.
import { createRequestHandler } from '@remix-run/architect'
import type { ServerBuild } from '@remix-run/node'
const build = require('./build') as ServerBuild
export const handler = createRequestHandler({
build,
getLoadContext() {
return {}
},
})
Esse arquivo será o handler da nossa Lambda e receberá todas as requisições do cliente.
Agora vamos ver o arquivo de configuração do serverless.
service: remix-serverless
frameworkVersion: '3'
plugins:
- serverless-esbuild
- serverless-s3-sync
provider:
name: aws
runtime: nodejs16.x
stage: ${opt:stage, 'dev'}
region: ${opt:region, 'us-east-1'}
custom:
esbuild:
bundle: true
minify: false
sourcemap: true
sourcesContent: false
exclude: ['aws-sdk']
target: 'node16'
platform: 'node'
s3Sync:
buckets:
- bucketNameKey: WebsiteBucketName
bucketPrefix: website/
localDir: public
functions:
remix:
handler: server/remix.handler
events:
- httpApi:
method: any
path: '/{proxy+}'
resources:
Resources:
HttpApi:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: HttpApi-${self:service}
ProtocolType: HTTP
WebsiteBucket:
Type: AWS::S3::Bucket
Properties: {}
WebsiteOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: Origin Access Identity to Access ${self:service} Website Bucket
WebsiteBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WebsiteBucket
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:GetObject
Resource:
Fn::Join:
- /
- - Fn::GetAtt:
- WebsiteBucket
- Arn
- '*'
Principal:
CanonicalUser:
Fn::GetAtt:
- WebsiteOriginAccessIdentity
- S3CanonicalUserId
CDN:
Type: AWS::CloudFront::Distribution
DependsOn:
- WebsiteBucket
- HttpApi
Properties:
DistributionConfig:
Enabled: true
Origins:
- DomainName:
Fn::GetAtt:
- WebsiteBucket
- RegionalDomainName
Id: StaticOrigin
S3OriginConfig:
OriginAccessIdentity:
Fn::Join:
- /
- - origin-access-identity
- cloudfront
- !Ref WebsiteOriginAccessIdentity
OriginPath: '/website'
- DomainName:
Fn::Join:
- ''
- - Ref: HttpApi
- '.execute-api.${self:provider.region}.amazonaws.com'
Id: RemixOrigin
CustomOriginConfig:
OriginProtocolPolicy: https-only
OriginSSLProtocols: [TLSv1.2]
DefaultCacheBehavior:
AllowedMethods: [GET, HEAD, OPTIONS, PUT, PATCH, POST, DELETE]
CachedMethods: [GET, HEAD, OPTIONS]
Compress: true
TargetOriginId: RemixOrigin
ViewerProtocolPolicy: redirect-to-https
ForwardedValues:
QueryString: true
Cookies:
Forward: none
Outputs:
WebsiteBucketName:
Value:
Ref: WebsiteBucket
DistributionID:
Value:
Ref: CDN
WebsiteDomain:
Value:
Fn::GetAtt: [CDN, DomainName]
Esse é o arquivo de configuração padrão do serverless. Com isso, você terá o mínimo necessário para conseguir subir sua aplicação na AWS.
Alguns pontos interessantes dessa configuração é que além de um Lambda que recebe as requisições, também estamos usando o CloudFront como CDN e o S3 para armazenar nossos arquivos estáticos. Sobre esse último, usamos o plugin serverless-s3-sync
para subir esses arquivos diretamente no bucket em todo deploy.
Top comments (1)
Boa Franco! Ficou maneiro demais esse tutorial cara... bora fazer mais :D