Razzle: Razzle 和无服务器

创建于 2018-05-30  ·  30评论  ·  资料来源: jaredpalmer/razzle

你好 !
我正在尝试在无服务器上运行一个 razzle 项目(materialUI、Typescript、Apollo 和 After)。
使用 Serverless 处理此问题的最佳方法是什么?

如果你知道任何例子,我很乐意看看。 谢谢 !

最有用的评论

这是我使用AWS CDK将 Razzle 应用程序部署到 AWS Lambda 的方式:

RazzleStack

import * as CDK from '@aws-cdk/core';
import * as S3 from '@aws-cdk/aws-s3';
import * as S3Deployment from '@aws-cdk/aws-s3-deployment';
import * as Lambda from '@aws-cdk/aws-lambda';
import * as APIGateway from '@aws-cdk/aws-apigateway';
import * as SSM from '@aws-cdk/aws-ssm';
import * as SecretsManager from '@aws-cdk/aws-secretsmanager';

import { ConfigProps, getParam, ModeStack } from '../helpers';

export class RazzleStack extends ModeStack {
  constructor(app: CDK.App, id: string, props: ConfigProps) {
    super(app, id, props);

    /**
     * S3 bucket to the /public folder
     */
    const publicBucketName = `my-razzle-app-bucket-public-files-${this.mode}`;
    const bucketPublicFiles = new S3.Bucket(this, publicBucketName, {
      publicReadAccess: true,
      bucketName: publicBucketName.toLowerCase(),
    });

    /**
     * Store S3 bucket name
     */
    new SSM.StringParameter(this, `MyRazzleAppBucketAssetsName${this.Mode}`, {
      description: `My Razzle App S3 Bucket Name for Assets on ${this.Mode}`,
      parameterName: `/${props.name}/S3/Assets/Name`,
      stringValue: bucketPublicFiles.bucketName,
    });

    /**
     * Store S3 domainName name
     */
    new SSM.StringParameter(this, `MyRazzleAppBucketAssetsDomainName${this.Mode}`, {
      description: `My Razzle App S3 Bucket DomainName for Assets on ${this.Mode}`,
      parameterName: `/${props.name}/S3/Assets/DomainName`,
      stringValue: bucketPublicFiles.bucketDomainName,
    });

    /**
     * Deploy public folder of build to `my-razzle-app-bucket-public-files-${this.mode}` bucket
     */
    new S3Deployment.BucketDeployment(this, `${publicBucketName}-deploy`, {
      sources: [S3Deployment.Source.asset('./build/public')],
      destinationBucket: bucketPublicFiles,
    });

    /**
     * Environment Variables for SSR Function
     */
    const environmentKeys = [
      'NODE_ENV',
      'RAZZLE_GRAPHQL_URL',
      'RAZZLE_MAPBOX_ACCESS_TOKEN',
      'RAZZLE_GOOGLE_RECAPTCHA_SITE',
      'RAZZLE_GA_ID',
    ];

    const environmentSecret = SecretsManager.Secret.fromSecretAttributes(
      this,
      `MyRazzleAppEnvironmentSecret${this.Mode}`,
      {
        secretArn: getParam(this, `MyRazzleAppSecretsArn${this.Mode}`),
      },
    );

    let environment: { [key: string]: string } = {};
    for (const key of environmentKeys) {
      environment[key] = environmentSecret.secretValueFromJson(key).toString();
    }

    /**
     * Razzle SSR Function
     */
    const myRazzleAppSsrFunction = new Lambda.Function(this, `MyRazzleAppSSRFunction${this.Mode}`, {
      description: `Lambda Function that runs My Razzle App SSR on ${this.Mode}`,
      code: Lambda.Code.fromAsset('./build', {
        exclude: ['public', 'static', '*.json'],
      }),
      handler: 'server.handler',
      runtime: Lambda.Runtime.NODEJS_12_X,
      memorySize: 512,
      timeout: CDK.Duration.seconds(5),
      environment,
      tracing: Lambda.Tracing.ACTIVE,
    });

    /**
     * Razzle ApiGateway
     */
    const razzleSsrApiGatewayName = `MyRazzleAppSSRApiGateway${this.Mode}`;
    const api = new APIGateway.RestApi(this, razzleSsrApiGatewayName, {
      description: `ApiGateway that exposes My Razzle App SSR on ${this.Mode}`,
      binaryMediaTypes: ['*/*'],
      endpointTypes: [APIGateway.EndpointType.REGIONAL],
      deployOptions: {
        stageName: this.mode,
      },
    });
    const integration = new APIGateway.LambdaIntegration(myRazzleAppSsrFunction);
    const root = api.root;
    const pathApi = api.root.addResource('{proxy+}');

    root.addMethod('GET', integration);
    pathApi.addMethod('ANY', integration);

    /**
     * Razzle ApiGateway ID
     */
    new SSM.StringParameter(this, `MyRazzleAppAPIGatewayRestId${this.Mode}`, {
      description: `My Razzle App ApiGateway ID on ${this.Mode}`,
      parameterName: `/${props.name}/APIGateway/ApiId`,
      stringValue: api.restApiId,
    });
  }
}

所有30条评论

有人刚刚这样做并在推特上发布了它。 将是一个很酷的例子

找不到推文! 你有吗 ?

是我的推文 - 我只是用 index.js(所有热加载发生的地方)替换了

import awsServerlessExpress from 'aws-serverless-express'
import app from './server'
const binaryMimeTypes = [
  'application/octet-stream',
  'font/eot',
  'font/opentype',
  'font/otf',
  'image/jpeg',
  'image/png',
  'image/svg+xml',
]
const server = awsServerlessExpress.createServer(app, null, binaryMimeTypes)

export const handler = (event, context, callback) => {
  awsServerlessExpress.proxy(server, event, context)
}

然后运行npm run build ,将node_modules/复制到build/ ,将server.jsnode_modules/文件夹一起压缩,然后将其上传到 AWS Lambda。 public/都会上传到 S3 并通过 Cloudfront 发送。

对于 Lambda -> API 网关基础设施,我使用了 Terraform 并遵循了本指南: https :

虽然仍然不完美 - 从客户端包(https://static.jobsok.io/)加载时出现此错误:

image


更新:
我不打扰你,删除 node_modules 并重新安装给了我:

File sizes after gzip:

  210.38 KB (+8 B)  build/static/js/bundle.1a960add.js
  1.96 KB           build/static/css/bundle.5865fae5.css

无需更改代码 - 使客户端包再次运行

我的 handler.js(或 index.js)是一样的。
然后,我用 webpack (project + node_modules) 构建所有东西。 (你只需要为此修改 razzle.config.js)

我正在使用无服务器,而不是 terraform。 基本一样。 只需要为处理程序导入相关包(由于某种原因,webpack 没有打包这个,可能是因为它在不同的文件/路径上,稍后会修复)。

无服务器上传所有这些,就是这样。 你可以在这里看到这个项目: https :

它远非完美,然后擦拭。

顺便说一句, @jaredpalmer ,你能帮我解决 razzle/webpack 配置吗?
我想以最佳性能包含捆绑包中所需的所有内容(包括 node_modules)。 任何例子/提示? (使用打字稿配置)

@rozenmd你能分享你的 razzle.config 吗?

@romainquellec我的是空的 - 除了 razzle 提供的之外没有额外的配置

好吧,我不知道为什么,但我无法访问依赖项中定义的 aws-serverless-express(可能更多)。
为了优化所有这些,我想从最终构建中排除 aws-sdk(它总是在 lambda 函数中可用,但稍后会看到)

我认为它来自外部,但我无法弄清楚。

是的,我有同样的问题。 我唯一的解决方案是使用所有node_modules 压缩 server.js

我不能在 lambda 上。 官方限制大小​​为 50 个月。
每个人的一般问题:如何将具有良好性能的 node_modules 与 razzle 捆绑在一起。 谢谢 !

我们不使用 lambda,但我们通过删除 razzle.config.js 中的默认 externals 选项来捆绑我们所有的 node_modules。 这很酷,因为这意味着构建文件夹实际上是一个构建工件。 然后我们将它放到 s3 上,然后我们的 Jenkins 任务将新文件夹拉到每个服务器上并重新启动 pm2。

@jaredpalmer - 你有没有可能用razzle.config.js发布一个要点?

通过删除 razzle.config.js 中的默认 externals 选项:

像那样 ?
modify(config, { target, dev }) { if (target === 'node' && !dev) { config.externals = []; } return config; }

@romainquellec有效!!

module.exports = {
  modify: (config, { target, dev }, webpack) => {
    // do something to config
    const appConfig = config // stay immutable here

    if (target === 'node' && !dev) {
      appConfig.externals = []
    }

    return appConfig
  },
}

它正在工作。 好的 ! 👍👍
添加一个例子是公平的! 我这个周末去看看。 你介意参与@rozenmd吗?

不幸的是我这个周末没有空闲时间@romainquellec但很高兴审查/合作 PRs

我正在关闭这个。 将尽快提交 PR。

@romainquellec你有没有绕过提出那个拉取请求? 您还在无服务器环境中运行 Razzle 项目吗? 我刚刚开始探索这种可能性并偶然发现了这个线程:)

@mbrochh 最后没有看到拉取请求,但我仍在https://onlineornot.com上使用 Razzle 进行我的所有无服务器服务器端渲染。

我也刚刚发现这篇博文: https: //medium.com/@RozenMD/build -your-own-app-with-react-graphql-and-serverless-architecture-part-1-server-side-rendering-f0d0144ff5f

我想我将不得不一头扎进去,并尝试关注该帖子:)

谢谢你的帖子!

https://maxrozen.com/2018/08/08/start-your-own-app-with-react-part-1可能是代码格式更好的链接。

谢谢! :微笑:

有没有人将 Razzle 部署到 AWS lambda?

我也在寻找 AWS lambda 上的 Razzle 示例。 如果有人做过并乐于与我们分享,将不胜感激。

这是我使用AWS CDK将 Razzle 应用程序部署到 AWS Lambda 的方式:

RazzleStack

import * as CDK from '@aws-cdk/core';
import * as S3 from '@aws-cdk/aws-s3';
import * as S3Deployment from '@aws-cdk/aws-s3-deployment';
import * as Lambda from '@aws-cdk/aws-lambda';
import * as APIGateway from '@aws-cdk/aws-apigateway';
import * as SSM from '@aws-cdk/aws-ssm';
import * as SecretsManager from '@aws-cdk/aws-secretsmanager';

import { ConfigProps, getParam, ModeStack } from '../helpers';

export class RazzleStack extends ModeStack {
  constructor(app: CDK.App, id: string, props: ConfigProps) {
    super(app, id, props);

    /**
     * S3 bucket to the /public folder
     */
    const publicBucketName = `my-razzle-app-bucket-public-files-${this.mode}`;
    const bucketPublicFiles = new S3.Bucket(this, publicBucketName, {
      publicReadAccess: true,
      bucketName: publicBucketName.toLowerCase(),
    });

    /**
     * Store S3 bucket name
     */
    new SSM.StringParameter(this, `MyRazzleAppBucketAssetsName${this.Mode}`, {
      description: `My Razzle App S3 Bucket Name for Assets on ${this.Mode}`,
      parameterName: `/${props.name}/S3/Assets/Name`,
      stringValue: bucketPublicFiles.bucketName,
    });

    /**
     * Store S3 domainName name
     */
    new SSM.StringParameter(this, `MyRazzleAppBucketAssetsDomainName${this.Mode}`, {
      description: `My Razzle App S3 Bucket DomainName for Assets on ${this.Mode}`,
      parameterName: `/${props.name}/S3/Assets/DomainName`,
      stringValue: bucketPublicFiles.bucketDomainName,
    });

    /**
     * Deploy public folder of build to `my-razzle-app-bucket-public-files-${this.mode}` bucket
     */
    new S3Deployment.BucketDeployment(this, `${publicBucketName}-deploy`, {
      sources: [S3Deployment.Source.asset('./build/public')],
      destinationBucket: bucketPublicFiles,
    });

    /**
     * Environment Variables for SSR Function
     */
    const environmentKeys = [
      'NODE_ENV',
      'RAZZLE_GRAPHQL_URL',
      'RAZZLE_MAPBOX_ACCESS_TOKEN',
      'RAZZLE_GOOGLE_RECAPTCHA_SITE',
      'RAZZLE_GA_ID',
    ];

    const environmentSecret = SecretsManager.Secret.fromSecretAttributes(
      this,
      `MyRazzleAppEnvironmentSecret${this.Mode}`,
      {
        secretArn: getParam(this, `MyRazzleAppSecretsArn${this.Mode}`),
      },
    );

    let environment: { [key: string]: string } = {};
    for (const key of environmentKeys) {
      environment[key] = environmentSecret.secretValueFromJson(key).toString();
    }

    /**
     * Razzle SSR Function
     */
    const myRazzleAppSsrFunction = new Lambda.Function(this, `MyRazzleAppSSRFunction${this.Mode}`, {
      description: `Lambda Function that runs My Razzle App SSR on ${this.Mode}`,
      code: Lambda.Code.fromAsset('./build', {
        exclude: ['public', 'static', '*.json'],
      }),
      handler: 'server.handler',
      runtime: Lambda.Runtime.NODEJS_12_X,
      memorySize: 512,
      timeout: CDK.Duration.seconds(5),
      environment,
      tracing: Lambda.Tracing.ACTIVE,
    });

    /**
     * Razzle ApiGateway
     */
    const razzleSsrApiGatewayName = `MyRazzleAppSSRApiGateway${this.Mode}`;
    const api = new APIGateway.RestApi(this, razzleSsrApiGatewayName, {
      description: `ApiGateway that exposes My Razzle App SSR on ${this.Mode}`,
      binaryMediaTypes: ['*/*'],
      endpointTypes: [APIGateway.EndpointType.REGIONAL],
      deployOptions: {
        stageName: this.mode,
      },
    });
    const integration = new APIGateway.LambdaIntegration(myRazzleAppSsrFunction);
    const root = api.root;
    const pathApi = api.root.addResource('{proxy+}');

    root.addMethod('GET', integration);
    pathApi.addMethod('ANY', integration);

    /**
     * Razzle ApiGateway ID
     */
    new SSM.StringParameter(this, `MyRazzleAppAPIGatewayRestId${this.Mode}`, {
      description: `My Razzle App ApiGateway ID on ${this.Mode}`,
      parameterName: `/${props.name}/APIGateway/ApiId`,
      stringValue: api.restApiId,
    });
  }
}

@jgcmarins关于那个 razzletack 设置。 你能一步一步地如何使用它吗? 我想将它添加到 razzle 文档中。

https://razzle-git-canary.jared.vercel.app/deployment-options/aws

我需要一些帮助,有人可以帮我吗?

@fivethreeo一步一步是在 AWS 账户上设置 CDK,然后运行cdk deploy并带有一些额外的标志,它是实时的 \o/

我想用域而不是路径来制作舞台。 也许用 rds、密钥轮换、TypeOrm、typegraphql、Formik 和 jwt 制作一个功能齐全的例子,但不是 razzle 本身的例子。

否则什么都没有。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

charlie632 picture charlie632  ·  4评论

piersolenski picture piersolenski  ·  4评论

JacopKane picture JacopKane  ·  3评论

gabimor picture gabimor  ·  3评论

dizzyn picture dizzyn  ·  3评论