A crash course on serverless-side rendering with React.js, Next.js and AWS Lambda

Adnan Rahić on December 03, 2018

Not so long ago I started exploring server-side rendered single-page applications. Yeah, try saying that three times fast. Building products for st... [Read Full]
markdown guide
 

Great tutorial - thank you! I finished the tutorial and successfully deployed, but am getting errors when navigating to the site. When navigating to https://[unique id].execute-api.us-east-1.amazonaws.com/dev/, I get the message {"message": "Internal server error"}. One nuance is that I removed the customDomain section, as I was hoping to use the AWS endpoint.

One thing I did when testing, is I added "dev": "next" to the scripts object in the package.json, so when I run npm run dev, I get the correct information displaying at localhost:3000, localhost:3000/dogs, etc.

Also, I added the code below to the server.js file, and when I navigate to https://[unique id].execute-api.us-east-1.amazonaws.com/dev/test, I get the 'Hello World!' message displaying, as expected:

server.get('/test', (req, res) => {
res.send('Hello World!');
});

The full server.js file is below. The server.get function with the res.send works, but the server.get functions with the app.render always throw an error.

const express = require('express');
const path = require('path');
const dev = process.env.NODE_ENV !== 'production';
const next = require('next');
const pathMatch = require('path-match');
const app = next({ dev });
const handle = app.getRequestHandler();
const { parse } = require('url');

const server = express();
const route = pathMatch();
server.use('/next', express.static(path.join(_dirname, '.next')));
server.get('/test', (req, res) => {
res.send('Hello World!');
});
server.get('/', (req, res) => {
app.render(req, res, '/')
});
server.get('/dogs', (req, res) => {
app.render(req, res, '/dogs')
});
server.get('/dogs/:breed', (req, res) => {
const params = route('/dogs/:breed')(parse(req.url).pathname)
return app.render(req, res, '/dogs/_breed', params)
});
server.get('*', (req, res) => handle(req, res))

module.exports = server;

Any insight would be greatly appreciated! Let me know if I can provide any additional information. Sorry if I'm doing anything real obvious, as I am completely new to Next.js and still fairly beginner with AWS.

 

I would add the same exact comment. I get Internal server error. If I try to add the /test get route, it works fine. If I try next locally it displays fine on localhost:3000. I myself has not even removed the customDomain section as I have a domain on Route 53 for that.

I have just tried copying the repo provided in the article and deploying it to my domain. Still the same Internal server error.

 

I get the same error when deploying the app to AWS.

Error:
{
message: "Internal server error"
}

The routes work fine locally, except the dynamic _breed route which results in a 404.

I've tried everything myself and also cloned the git repo as is to test. Both give the exact same result mentioned above. I have a custom domain linked via "sls create_domain" command.

 

for local development and testing:
npm run dev will run it has a next app without serverless (only pages are available, no serverless functionality written in server.js). You need to use serverless-offline github.com/dherault/serverless-off...

 

Same here. I'm getting the internal server error and the issue has to do with calling app.render. Further research leads me to believe that the problem stems from not calling app.prepare before configuring the server.

 

Hi, this raised a couple a questions with me:

  1. What benefit does serverless provide in this scenario? What are the alternatives(aws cli?terraform?idk.)?
  2. Why is your article on nuxt mentioned only in the list at the end, although this is almost exactly the same thing again?
 

Hey, glad to answer.

  1. The Serverless Framework makes it easy to deploy resources and create a domain. You can use whatever tool you like, AWS SAM, the AWS CLI, CloudFormation, Terraform, etc. It's totally up to you what you want to use. Using the Serverless Framework lowers the barrier to entry for devs who are not familiar to the services and tools AWS provides. It's just easier to get started with.
  2. It's mentioned at the end solely because Vue and React are two totally different frameworks with different ecosystems. I didn't want to create confusion. Vue devs can follow the Nuxt guide, while React devs can follow this one. :)

Hope this answers your questions. Feel free to let me know if you have any more. :D

 

I have stumbled on a few of your tutorials Adnan and they are all awesome. I actually read through all of this before realising it was you (I got into Serverless when I read your Express, Mongo + Serverless tutorial on hackernoon). NextJS is great and I am stoked that it is just as easy to deploy to Lambda. Thank you!

 

Hey Gary! Wow, thanks for the kind words. I'm glad I can help the community understand serverless architectures. Stay tuned for more tutorials. :)

 

Hi,

Once I deploy this, I'm able to load the front page, but all the next resources (*.js) come back as 403, any idea why?

Thanks for your help,

 

FWIW, you're probably seeing issue from not deploying with custom domain and needing to have the stage in the path to access the resource, e.g.
https://XXXXX.execute-api.us-east-1.amazonaws.com/production/_next/static/YYYYY/pages/index.js is needed but https://XXXXX.execute-api.us-east-1.amazonaws.com/_next/static/YYYYY/pages/index.js is what the express response actually renders.

This explains some of issue and attempts to make it work github.com/dougmoscrop/serverless-...

as well as this: github.com/zeit/next.js/issues/6447

 

Can you try cloning the repo and deploying it? If that works, there has to be a strange issue somewhere in your code. Compare your project to the cloned repo. Hopefully, that'll help you out. :)

 

It doesn't work when I clone it from the repo either. The only thing I've changed were the values in secret.json

On local routes work fine, except for the dynamic "_breed" route. It results in 404 Not Found.

On deploying to AWS all routes result in message: "Internal server error" ... you can check it out at ecom.ideamappers.com

 

Hi - this doesn't work.

The only route that does work (without defining a specific route definition in server.js) is index.js.

Any other pages under /pages fails to render with a 404, although, they do work fine in local environment.

Perhaps there's some other settings that aren't listed in this article?

 

That does not work. It gives and internal server error as soon as you try using app.render.

 

Hi,

I have exactly the same issue.
Where you able to solve it?

Thanks

 

This was the error I received after I ran 'npm run deploy' for the first time.

I have a domain name that ends in .info

Could that be the cause?


Error: Could not set up basepath mapping. Try running sls create_domain first.

Error: Error: 'react-ssr.happyweather.info' could not be found in API Gateway.

NotFoundException: Invalid domain name identifier specified
 

Have you done any performance testing on this setup? I would be interested to know some numbers like TTFB and others.

 

I haven't actually. I'd be stoked to know the performance too. Once I find the time, I'll surely check it out. If you get around to doing it yourself, please let me know! :D

 

Is it possible to display images from the static folder once the site is deployed? I added an image for a logo to the static folder and am displaying it in the header component. It shows while I'm developing on localhost, but when I deploy to AWS it shows as a broken image.

Here's my code:
github.com/joshglazer/freetimeupda...

 

I figured this out myself. In case anyone's curious, you can fix this by adding the following line of code to your server.js file.

server.use('/static', express.static('static'))

 

I saw that Next.js 8 was released after you published this article. It has a section about how serverless is now supported. Should I do anything differently because of this?

 

Considering lambda cold starts; what does this do for initial load times?

 

So you keep the secrets file locally? What if you are on a team and want to deploy this through CI/CD? Would you handle that on there?

 

You use AWS KMS. Here's a nice Serverless plugin. :)

nordcloud / serverless-kms-secrets

🔑🔐☁️ Serverless plugin to encrypt variables with KMS

Serverless KMS Secrets

A Serverless Plugin for the Serverless Framework which helps with encrypting service secrets using the AWS Key Management Service (KMS)

Introduction

This plugins does the following:

  • It provides commands to encrypt and decrypt secrets with KMS

Installation and configuration

In your service root, run:

npm install --save-dev serverless-kms-secrets

Add the plugin to serverless.yml:

plugins
  - serverless-kms-secrets

Configure the plugin into the custom block in serverless.yml. For example:

custom
  serverless-kms-secrets
    secretsFile: kms-secrets.${opt:stage, self:provider.stage}.${opt:region, self:provider.region}.yml (optional)
  kmsSecrets: ${file(kms-secrets.${opt:stage, self:provider.stage}.${opt:region, self:provider.region}.yml)}

By default, the plugin creates secrets to the file kms-secrets.[stage].[region].yml. This can be overriden with the secretsFile parameter in the serverless-kms-secrets configuration.

Add Decrypt permissions to your lambda function with e.g. this block in IamRoleStatements:

    - Effect: Allow
      Action:
      - KMS:Decrypt
      Resource: ${self:custom.kmsSecrets.keyArn} 

Usage

Creating KMS Key

Create a KMS key in AWS IAM service, under Encryption keys. Collect…

code of conduct - report abuse