Razzle: Razzle und serverlos

Erstellt am 30. Mai 2018  ·  30Kommentare  ·  Quelle: jaredpalmer/razzle

Hallo !
Ich versuche, ein Razzle-Projekt (materialUI, Typescript, Apollo & After) serverlos auszuführen.
Was ist der beste Weg, dies mit Serverless zu handhaben?

Wenn Sie ein Beispiel kennen, freue ich mich, einen Blick darauf zu werfen. Vielen Dank !

Hilfreichster Kommentar

So habe ich Razzle-Apps mit AWS CDK in 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,
    });
  }
}

Alle 30 Kommentare

Jemand hat das gerade gemacht und darüber getwittert. Wäre ein cooles Beispiel

Kann den Tweet nicht finden! Hast Du es ?

War mein Tweet - ich habe gerade index.js (wo das ganze Hotloading passiert) ersetzt durch

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)
}

Anschließend führen Sie npm run build , kopieren node_modules/ nach build/ , zippen server.js zusammen mit dem Ordner node_modules/ und laden ihn in AWS Lambda hoch. Alles in public/ dann auf S3 hochgeladen und über Cloudfront gesendet.

Für Lambda -> API-Gateway-Infrastruktur habe ich Terraform verwendet und dieser Anleitung gefolgt: https://www.terraform.io/docs/providers/aws/guides/serverless-with-aws-lambda-and-api-gateway.html

Trotzdem immer noch nicht perfekt - diesen Fehler beim Laden aus dem clientseitigen Bundle (https://static.jobsok.io/) erhalten:

image


Aktualisieren:
Ich scheiße dich nicht, das Löschen von node_modules und die Neuinstallation gab mir:

File sizes after gzip:

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

Ohne Codeänderungen - das clientseitige Bundle wieder funktionsfähig gemacht

Meine handler.js (oder index.js) ist die gleiche.
Dann baue ich alles mit Webpack (project + node_modules). (Sie müssen dafür nur razzle.config.js ändern)

Ich arbeite mit Serverless, nicht mit Terraform. Im Grunde gleich. Sie müssen nur das relative Paket für den Handler importieren (aus irgendeinem Grund packt Webpack dieses nicht, vielleicht weil es sich in einer anderen Datei / einem anderen Pfad befindet, wird später behoben).

Serverless Upload all dies, und das war's. Sie können das Projekt hier sehen: https://github.com/CuistotduCoin/cuistot

Es ist alles andere als perfekt, und wip.

Übrigens, @jaredpalmer , kannst du mir bei der razzle/webpack-Konfiguration helfen?
Ich möchte alles enthalten, was im Bundle benötigt wird (node_modules enthalten) mit bester Leistung. Irgendwelche Beispiele/Tipps? (Mit Typescript-Konfiguration)

@rozenmd kannst du deine razzle.config teilen?

@romainquellec Mine ist leer - keine zusätzliche Konfiguration über das hinaus, was razzle bietet

Nun, ich weiß nicht warum, aber ich kann nicht auf aws-serverless-express zugreifen, das in Abhängigkeiten (vielleicht mehr) definiert ist.
Und um all das zu optimieren, möchte ich aws-sdk vom endgültigen Build ausschließen (es ist immer in einer Lambda-Funktion verfügbar, werde das später sehen)

Ich denke, es kommt von extern, aber ich kann es nicht herausfinden.

Ja, ich hatte das gleiche Problem. Meine einzige Lösung bestand darin, die server.js mit allen node_modules zu zippen

Ich kann nicht auf Lambda. Offizielle Limitgröße ist 50mo.
Allgemeine Frage an alle: Wie man node_modules mit guter Perf mit Razzle bündelt. Vielen Dank !

Wir verwenden kein Lambda, aber wir bündeln alle unsere node_modules, indem wir die Standardoption externals in razzle.config.js entfernen. Das ist cool, weil es bedeutet, dass der Build-Ordner effektiv ein Build-Artefakt ist. Wir legen es dann auf s3, dann zieht unsere Jenkins-Aufgabe den neuen Ordner auf jeden Server und startet pm2 neu.

@jaredpalmer - hast du die Möglichkeit, einen Inhalt mit diesem razzle.config.js posten?

durch Entfernen der Standardoption externals in razzle.config.js:

So wie das ?
modify(config, { target, dev }) { if (target === 'node' && !dev) { config.externals = []; } return config; }

@romainquellec das funktioniert!!

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
  },
}

Es funktioniert. Gut ! 👍 👍
Es ist fair, ein Beispiel hinzuzufügen! Das schaue ich mir dieses Wochenende an. Macht es Ihnen etwas aus, @rozenmd mitzumachen ?

Leider habe ich dieses Wochenende keine Freizeit @romainquellec, aber ich

Ich schließe das. Werde schnellstmöglich eine PR einreichen.

@romainquellec bist du jemals dazu gekommen, diese Pull-Anfrage zu stellen? Führen Sie Ihr Razzle-Projekt immer noch in einer serverlosen Umgebung aus? Ich fange gerade erst an, diese Möglichkeit zu erkunden und bin über diesen Thread gestolpert :)

@mbrochh Ich habe am Ende keinen Pull-Request gesehen, aber ich verwende Razzle immer noch auf https://onlineornot.com für mein serverloses serverseitiges Rendering.

Ich habe auch gerade diesen Blogbeitrag gefunden: https://medium.com/@RozenMD/build -your-own-app-with-react-graphql-and-serverless-architecture-part-1-server-side-rendering-f0d0144ff5f

Ich denke, ich muss kopfüber eintauchen und versuchen, diesem Beitrag zu folgen :)

Danke für den Beitrag!

https://maxrozen.com/2018/08/08/start-your-own-app-with-react-part-1 ist wahrscheinlich ein besserer Link für die Codeformatierung.

und danke! :Lächeln:

Hat jemand Razzle für AWS Lambda bereitgestellt?

Ich suche auch ein Beispiel für Razzle on AWS Lambda. Wenn es jemand getan hat und gerne mit uns teilt, wäre ich sehr dankbar.

So habe ich Razzle-Apps mit AWS CDK in 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 über dieses Razzletack-Setup. Könnten Sie Schritt für Schritt tun, wie Sie dies verwenden können? Ich möchte es zu Razzle-Dokumenten hinzufügen.

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

Ich brauche etwas Hilfe dabei, kann mir jemand helfen?

@fivethreeo der Schritt für Schritt wäre, CDK auf einem AWS-Konto cdk deploy mit einigen zusätzlichen Flags auszuführen und es ist live \o/

Ich möchte die Bühne mit einer Domäne statt mit einem Pfad machen. Vielleicht machen Sie ein voll funktionsfähiges Beispiel mit rds, Key Rotation, TypeOrm, typegraphql, Formik und jwt, aber nicht als Beispiel in razzle selbst.

Sonst fehlt nichts.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

GouthamKD picture GouthamKD  ·  3Kommentare

sebmor picture sebmor  ·  4Kommentare

panbanda picture panbanda  ·  5Kommentare

howardya picture howardya  ·  5Kommentare

charlie632 picture charlie632  ·  4Kommentare