dioxus-aws

Crates.iodioxus-aws
lib.rsdioxus-aws
version0.6.4
sourcesrc
created_at2024-09-18 03:48:45.36763
updated_at2024-11-04 18:31:34.81647
descriptionLibrary for deploying Dioxus apps to AWS Serverless (AWS Lambda, S3, CloudFront, Route53).
homepage
repositoryhttps://github.com/hackartists/dioxus-aws.git
max_upload_size
id1378695
size20,554
hackartist (hackartists)

documentation

README

Dioxus AWS

dioxus-aws crate provides a launch function to make dioxus run on AWS Serverless Stack (AWS Lambda, CloudFront and S3).

Development

Creating a project

  • Use dx command.
    • Currently, dioxus-cli v0.5, which is stable version of dioxus, is supported.
cargo install dioxus-cli --version ^0.5
dx new --subtemplate Fullstack project-name

Re-Writing main function

Installing dependancy

  • Add dioxus-aws.
cargo add dioxus-aws

main function

  • Change launch(App) to dioxus_aws::launch(App).
dioxus_aws::launch(App); // launch(App);

Running and testing the project

  • It is same with usage of dioxus-cli.
cargo add dioxus-aws
dx serve --platform fullstack

Deployment

It uses AWS Lambda, S3 and CloudFront to deploy dixous application.

Setup AWS CDK

  • Setup AWS CDK to deploy application.
npm install -g aws-cdk
mkdir -p fixtures/aws-cdk
cd fixtures/aws-cdk
cdk init app --language=typescript

Writing CDK Stack

  • Route53 sets up a domain to CloudFront.
  • CloudFront distributes requests to API Gateway or S3.
  • API Gateway is bound to AWS Lambda.
  • S3 stores assets like js, html, images and so on.
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";

import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as acm from "aws-cdk-lib/aws-certificatemanager";
import * as route53 from "aws-cdk-lib/aws-route53";
import * as targets from "aws-cdk-lib/aws-route53-targets";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as apigateway from "aws-cdk-lib/aws-apigateway";

export class AwsCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const domain = process.env.DOMAIN || "";
    const acmId = process.env.ACM_ID || "";
    const hostedZoneId = process.env.HOSTED_ZONE_ID || "";
    const projectRoot = process.env.PROJECT_ROOT || "";

    const assetsBucket = new s3.Bucket(this, "Bucket", {
      bucketName: domain,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });

    const certificate = acm.Certificate.fromCertificateArn(
      this,
      "Certificate",
      acmId,
    );

    const func = new lambda.Function(this, "Function", {
      runtime: lambda.Runtime.PROVIDED_AL2023,
      code: lambda.Code.fromAsset(projectRoot + "/dist"),
      handler: "bootstrap",
      environment: {
        NO_COLOR: "true",
        ASSETS_PATH: "./",
      },
      memorySize: 128,
    });

    const api = new apigateway.LambdaRestApi(this, "Api", {
      handler: func,
      proxy: true,
    });

    const s3Origin = new origins.S3Origin(assetsBucket);
    const apiOrigin = new origins.RestApiOrigin(api);

    const cf = new cloudfront.Distribution(this, "Distribution", {
      defaultBehavior: {
        origin: apiOrigin,
        cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
        allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
        cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
        originRequestPolicy:
          cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
      },
      additionalBehaviors: {
        "/assets/*": {
          origin: s3Origin,
          cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        },
        "/*.js": {
          origin: s3Origin,
          cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        },
        "/*.css": {
          origin: s3Origin,
          cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        },
        "/*.html": {
          origin: s3Origin,
          cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        },
        "/*.ico": {
          origin: s3Origin,
          cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        },
        "/*.svg": {
          origin: s3Origin,
          cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        },
        "/icons/*": {
          origin: s3Origin,
          cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        },
        "/images/*": {
          origin: s3Origin,
          cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        },
      },
      domainNames: [domain],
      certificate,
    });

    const zone = route53.HostedZone.fromHostedZoneAttributes(
      this,
      "zone-attribute",
      {
        zoneName: domain,
        hostedZoneId,
      },
    );

    new route53.ARecord(this, "IpV4Record", {
      zone,
      target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(cf)),
    });

    new route53.AaaaRecord(this, "IpV6Record", {
      zone,
      target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(cf)),
    });
  }
}

Build and deploy application

Building a binary for AWS Lambda

  • Note that server-feature is set to lambda instead of server.
  • Then, rename binary to bootstrap.
    • Usually, SERVICE might be basename $(git rev-parse --show-toplevel).
export SERVICE=$(cargo tree | head -n 1 | awk '{print $1}')
dx build --release --platform fullstack --server-feature lambda
mv dist/$SERVICE dist/bootstrap

Deploy AWS CDK

  • Let you remember environments in CDK Stack.
    • DOMAIN is FQDN including subdomain.
    • ACM_ID must be placed in us-east-1 for CloudFront.
    • HOSTED_ZONE_ID is a zone ID of Route53.
    • PROJECT_ROOT is a path of project root.
export DOMAIN="dioxus.example.com"
export ACM_ID="arn:aws:acm:us-east-1:---"
export HOSTED_ZONE_ID="Z--"
export PROJECT_ROOT=$(pwd)
export AWS_PROFILE=default

cd fixtures/aws-cdk
npm run build
cdk synth
cdk bootstrap --profile $AWS_PROFILE

# AWS Stack deployment
cdk deploy --require-approval never --profile $AWS_PROFILE

# S3 Assets sync
aws s3 sync $PROJECT_ROOT/dist/public s3://$DOMAIN --delete --profile $AWS_PROFILE
Commit count: 7

cargo fmt