DEV Community

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

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...
Collapse
 
dallashuggins profile image
Dallas Huggins • Edited

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.

Collapse
 
drumline18 profile image
drumline18 • Edited

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.

Collapse
 
hzburki profile image
Haseeb Burki

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.

Collapse
 
shood profile image
s-hood • Edited

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...

Collapse
 
its_ku profile image
Mo City Donald

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.

Collapse
 
qm3ster profile image
Mihail Malo

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?
Collapse
 
adnanrahic profile image
Adnan Rahić

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

Collapse
 
arswaw profile image
Arswaw

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
Collapse
 
sebas profile image
Sebastian Mantilla

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,

Collapse
 
clarsen profile image
Case Larsen • Edited

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

Collapse
 
adnanrahic profile image
Adnan Rahić

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. :)

Collapse
 
hzburki profile image
Haseeb Burki

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

Collapse
 
drumline18 profile image
drumline18

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

Collapse
 
joachimgoo profile image
joachimgoo

Hi,

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

Thanks

Collapse
 
garyburgmann profile image
Gary Burgmann

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!

Collapse
 
adnanrahic profile image
Adnan Rahić

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

Collapse
 
ivanoats profile image
Ivan Storck

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

Collapse
 
adnanrahic profile image
Adnan Rahić

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

Collapse
 
arswaw profile image
Arswaw

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?

Collapse
 
jsteixeira profile image
Joao

Hey, this is great thanks! For those following the tutorial and copying and pasting the code, there is a typo in index.js.. it is missing ). FYI

Collapse
 
adnanrahic profile image
Adnan Rahić

Oh wow, nice catch! Thanks for that. I've gone in and fixed the typo. I'm glad you like the tutorial. :)

Collapse
 
joshglazer profile image
Josh Glazer

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...

Collapse
 
joshglazer profile image
Josh Glazer

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

Collapse
 
felixseip profile image
Felix Seip • Edited

Great tutorial - thanks alot! I am new to the NextJS x AWS Lambda stack. When I call the function url, all I get is a random string like: PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KPGhlYWQ+CjxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4KPHRpdGxlPkVycm9yPC90aXRsZT4KPC9oZWFkPgo8Ym9keT4KPHByZT5DYW5ub3QgR0VUIC88L3ByZT4KPC9ib2R5Pgo8L2h0bWw+Cg==

Locally everything works fine and I can see that NextJS is rendering my pages. However, on lambda all I get is that random string.
Do you know why this could be happening? Thanks alot :D

Collapse
 
bitttttten profile image
bitten

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?

Collapse
 
adnanrahic profile image
Adnan Rahić

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…

Collapse
 
grath90 profile image
Cole

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