Razzle: Razzle et sans serveur

Créé le 30 mai 2018  ·  30Commentaires  ·  Source: jaredpalmer/razzle

Salut !
J'essaie d'exécuter un projet razzle (materialUI, Typescript, Apollo & After) sur serverless.
Quelle est la meilleure façon de gérer cela avec Serverless ?

Si vous connaissez un exemple, je suis heureux d'y jeter un œil. Merci !

Commentaire le plus utile

Voici comment j'ai déployé des applications Razzle sur AWS Lambda avec AWS CDK :

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

Tous les 30 commentaires

Quelqu'un vient de le faire et a tweeté à ce sujet. Ce serait un bon exemple

Je ne trouve pas le tweet ! L'avez vous ?

Était mon tweet - je viens de remplacer index.js (où tout le chargement à chaud se produit) par

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

Vous exécutez ensuite npm run build , copiez node_modules/ dans build/ , compressez server.js avec le dossier node_modules/ et chargez-le sur AWS Lambda. Tout dans public/ ensuite téléchargé sur S3 et envoyé via Cloudfront.

Pour l'infrastructure de passerelle Lambda -> API, j'ai utilisé Terraform et j'ai suivi ce guide : https://www.terraform.io/docs/providers/aws/guides/serverless-with-aws-lambda-and-api-gateway.html

Toujours pas parfait cependant - obtenir cette erreur au chargement à partir du bundle côté client (https://static.jobsok.io/):

image


Mettre à jour:
Je te chie pas, supprimer node_modules et réinstaller m'a donné:

File sizes after gzip:

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

Sans modification du code - a rendu le bundle côté client fonctionnel à nouveau

Mon handler.js (ou index.js) est le même.
Ensuite, je construis le tout avec webpack (project + node_modules). (Vous avez juste besoin de modifier razzle.config.js pour cela)

Je travaille avec serverless, pas terraform. Fondamentalement le même. Il suffit d'importer le package relatif pour le gestionnaire (pour une raison quelconque, webpack ne contient pas celui-ci, peut-être parce qu'il se trouve sur un fichier / chemin différent, sera corrigé plus tard).

Téléchargez tout cela sans serveur, et c'est tout. Vous pouvez voir le projet ici : https://github.com/CuistotduCoin/cuistot

C'est loin d'être parfait, et wip.

Au fait, @jaredpalmer , pouvez-vous m'aider sur la configuration de razzle/webpack ?
Je souhaite inclure tout ce dont j'ai besoin dans le bundle (node_modules inclus) avec les meilleures performances. Des exemples/conseils ? (Avec la configuration Typescript)

@rozenmd pouvez-vous partager votre razzle.config ?

@romainquellec la mine est vide - pas de configuration supplémentaire au-delà de ce que razzle fournit

Eh bien, je ne sais pas pourquoi, mais je ne peux pas accéder à aws-serverless-express défini dans les dépendances (peut-être plus).
Et pour optimiser tout ça, je veux exclure aws-sdk de la version finale (c'est toujours disponible dans une fonction lambda, je verrai ça plus tard)

Je pense que cela vient de l'extérieur, mais je n'ai pas compris.

Oui, j'ai eu le même problème. Ma seule solution était de compresser le server.js avec tous les node_modules

Je ne peux pas sur lambda. La taille limite officielle est de 50 mo.
Question générale pour tout le monde : Comment regrouper node_modules avec de bonnes performances avec razzle. Merci !

Nous n'utilisons pas lambda, mais nous regroupons tous nos node_modules en supprimant l'option externals par défaut dans razzle.config.js. C'est cool car cela signifie que le dossier de construction est effectivement un artefact de construction. Nous le mettons ensuite sur s3, puis notre tâche Jenkins extrait le nouveau dossier sur chaque serveur et redémarre pm2.

@jaredpalmer - avez-vous une chance de publier un résumé avec ce razzle.config.js ?

en supprimant l'option externals par défaut dans razzle.config.js :

Comme ça ?
modify(config, { target, dev }) { if (target === 'node' && !dev) { config.externals = []; } return config; }

@romainquellec ça marche !!

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

Ça marche. Bon ! 👍
C'est juste d'ajouter un exemple ! Je regarderai ça ce week-end. Ça te dérange de participer à

Malheureusement, je n'aurai pas de temps libre ce week-end @romainquellec mais heureux de revoir/collaborer sur les

Je ferme ça. Soumettra un PR dès que possible.

@romainquellec avez-vous déjà fait cette pull request ? Exécutez-vous toujours votre projet Razzle dans un environnement sans serveur ? Je commence juste à explorer cette possibilité et je suis tombé sur ce fil :)

@mbrochh Je n'ai https://onlineornot.com pour tout mon rendu côté serveur sans serveur.

Je viens également de trouver cet article de blog : https://medium.com/@RozenMD/build -your-own-app-with-react-graphql-and-serverless-architecture-part-1-server-side-rendering-f0d0144ff5f

Je suppose que je vais devoir plonger la tête la première et essayer de suivre ce post :)

Merci pour le post!

https://maxrozen.com/2018/08/08/start-your-own-app-with-react-part-1 est probablement un meilleur lien pour le formatage du code.

et merci! :le sourire:

Quelqu'un a-t-il déployé Razzle sur AWS lambda ?

Je recherche également un exemple pour Razzle sur AWS lambda. Si quelqu'un l'a fait et heureux de partager avec nous, je l'apprécierais vraiment.

Voici comment j'ai déployé des applications Razzle sur AWS Lambda avec AWS CDK :

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 à propos de cette configuration de razzletack. Pourriez-vous faire une étape par étape comment l'utiliser? Je voudrais l'ajouter à razzle docs.

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

J'ai besoin d'aide à ce sujet, quelqu'un peut-il me donner un coup de main?

@fivethreeo, l'étape par étape consisterait à configurer CDK sur un compte AWS, puis à exécuter cdk deploy avec quelques indicateurs supplémentaires et c'est en direct \o/

Je voudrais faire l'étape avec un domaine au lieu d'un chemin. Peut-être faire un exemple complet avec rds, rotation des clés, TypeOrm, typegraphql, Formik et jwt mais pas comme exemple dans razzle lui-même.

Rien ne manque sinon.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

dizzyn picture dizzyn  ·  3Commentaires

howardya picture howardya  ·  5Commentaires

knipferrc picture knipferrc  ·  5Commentaires

kkarkos picture kkarkos  ·  3Commentaires

jcblw picture jcblw  ·  4Commentaires