Local Dev Server for AWS Lambdas - node.js

Is there a dev server that runs AWS Lambdas locally? My requirements would be
nodejs server, no ruby or go or anything need to install other than node and npm packages
Creates a server that I can query via wget / curl or an API testing tool to send various events to
I should be able to specify a js file that the server uses as lambda and the server should restart / update when I change that file

Here's a solution that does not require serverless or claudiajs.
I usually just write my own little express script for this purpose. I always just use Lambda Proxy integration so it's simpler.
Something like this...
const bodyParser = require('body-parser')
const express = require('express')
// Two different Lambda handlers
const { api } = require('../src/api')
const { login } = ('../src/login')
const app = express()
app.use(bodyParser.json())
// route and their handlers
app.post('/login', lambdaProxyWrapper(login))
app.all('/*', lambdaProxyWrapper(api))
app.listen(8200, () => console.info('Server running on port 8200...'))
function lambdaProxyWrapper(handler) {
return (req, res) => {
// Here we convert the request into a Lambda event
const event = {
httpMethod: req.method,
queryStringParameters: req.query,
pathParameters: {
proxy: req.params[0],
},
body: JSON.stringify(req.body),
}
return handler(event, null, (err, response) => {
res.status(response.statusCode)
res.set(response.headers)
return res.json(JSON.parse(response.body))
})
}
}
Then, run it with nodemon so it watches the files and reloads as necessary.
nodemon --watch '{src,scripts}/**/*.js' scripts/server.js

Have you checked out SAM Local? https://github.com/awslabs/aws-sam-local

Related

Node JS post API endpoint not recognized in front end

I'm trying to make a post request using appwrite SDK in Node JS express and Vue JS. The SDK requires me to create an api post request to create new storage bucket in appwrite. The DOCs for this particular request isn't explaining really how to create the api in node JS express. I'm really new to Node JS and I already succeeded at creating get request but whenever I create the post request I get 404 not found error.
Node JS express file (server.js):
In this file there is get users request API which works perfectly fine.
And there is create bucket post request which when being called in frontend it comes back with a 404
const express = require("express");
const path = require("path");
const app = express(),
bodyParser = require("body-parser");
port = 3080;
// Init SDK
const sdk = require("node-appwrite");
let client = new sdk.Client();
let users = new sdk.Users(client);
let storage = new sdk.Storage(client);
client
.setEndpoint("http://localhost/v1") // Your API Endpoint
.setProject("tailwinder") // Your project ID
.setKey(
"Secrer Key!"
); // Your secret API key
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "../appwrite-app/build")));
//This get request works fine
//get user by ID
app.get("/v1/users/:id", (req, res) => {
let promise = users.get(req.params.id);
promise.then(
function (response) {
res.json(response);
},
function (error) {
console.log(error);
}
);
});
//This one isn't recognised in frontend
app.post("/v1/storage/buckets", function (req, res) {
let promise = storage.createBucket("bucket_id", "bucket_name", "file");
promise.then(
function (response) {
res.json(response);
},
function (error) {
console.log(error);
}
);
});
app.listen(port, () => {
console.log(`Server listening on the port::${port}`);
});
bucketsServices.js:
Here I'm using fetch post request to the api endpoint but it's not working.
export async function createBucket() {
const response = await fetch("/v1/storage/buckets", {
method: "POST",
});
return await response.json();
}
Addcomponent.vue:
Here I'm calling out the createBucket function from vue js file
bucketTesting() {
createBucket().then((response) => {
console.log(response);
});
},
The error which I assume it means that it's not reading my node js express post API:
bucketsService.js?993b:2 POST http://localhost:8080/v1/storage/buckets 404 (Not Found)
Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
A screenshot of the same error:
Something is missing here and I can't really figure it out.
You are making request to localhost:8080 meanwhile your server is running at localhost:3080
I believe your vue is running at port 8080 that's why /v1/storage/buckets gets prefixed by localhost:8080
Try to provide full URL while making request
export async function createBucket() {
const response = await fetch("localhost:3080/v1/storage/buckets", {
method: "POST",
});
return await response.json();
}
Better way might be to add proxy to automatically redirect request to correct URL, but this should work for now. This article might help with how to setup proxy in vue

nodejs - How to correctly serve vue app from express

I have a cli script that I'm converting to a local webapp using express to run a local server and pkg to create an executable that will include all the needed files. I'm using vue for the front-end and during the app development I didn't have any issue. I've tried to do a test build and when I try to launch the app I will get this error message in the opened browser tab Error: cannot GET /.
In my express routes I didn't created the default path for the root / so I suppose that the problem is with this aspect. I've correctly added the express.static() middleware but I'm not sure if I need to send the index.html file that run the vue app using express. How I configure correctly the root endpoint to serve the vue app?
Another small thing about the problem, how I can launch google chrome or default browser in linux using the exec() function of node child_process module?
Can anyone help me please?
async openBrowser(){
this.app.use(express.static(this.assets));
this.app.use(express.json());
this.app.use(express.urlencoded({extended: true}));
http.createServer(this.app).listen(8990, () => {
console.log(chalk.yellowBright('Starting quizist...'));
switch(process.platform){
case 'darwin':
this.child = exec('open --new -a "Google Chrome" --args "http://localhost:8990"');
break;
case 'win32':
this.child = exec('start chrome http://localhost:8990');
break;
}
});
// I have added the root endpoint but it's not clear for me how to serve the vue app
this.app.get('/', (req, res) => {
});
this.app.get('/init', (req, res) => {
this.extractQuestions();
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(this.askQuestions));
});
this.app.post('/checkanswer', async (req, res) => {
this.userAnswer = req.body;
const data = await this.checkAnswers();
res.setHeader('Content-Type','applications/json');
res.send(JSON.stringify(data));
});
this.app.get('/results', (req, res) => {
const data = {
userScore: this.score,
questionsNumber: this.askQuestions.length,
answeredQuestions: this.answeredQuestions,
correctAnswers: this.correctAnswers
}
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(data));
});
}

How to write api request tests without launching server?

It is possible to launch server on port and test it with library "supertest".
I'm wondering if it's possible to do the same without running a server?
Express app, the same as fastify app or others, takes request and response arguments from native node under the hood, like this:
const server = http.createServer((req, res) => {
res.end('hello\n');
});
So it should be possible to call that callback directly from tests.
How to get that callback? Is there any toolkit to help with constructing request and response objects for the callback?
The reason - just to make tests faster. As I don't want to test HTTP protocol and internals of node, I'd like to save time by skipping it.
Fastify has this feature out of the box:
'use strict'
const { test } = require('tap')
const Fastify = require('fastify')
test('requests the "/" route', async t => {
const fastify = Fastify()
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
const response = await app.inject({
method: 'GET',
url: '/'
})
t.strictEqual(response.statusCode, 200, 'returns a status code of 200')
})
To moch the req/resp object it is used light-my-request under the hood

Next.js server side api call returns 500 internal server error

I'm finally dipping my toe into the world of server side react using Next.js, however I'm pretty stumped with this issue.
I'm making a call to an API from pages/customer-preferences.tsx using isomorphic-unfetch
CustomerPreferencesPage.getInitialProps = async () => {
const res = await fetch(API_URL + '/preference-customer');
const initialData = await res.json();
return { initialData };
};
All works fine locally in dev mode or once built and ran build > start. To host it I'm running it from a docker container node:10, and when I run this locally all is fine also. The issue only happens once it's deployed.
When I navigate to / and then click a link to /customer-preferences all works as expected. But if I refresh the page or load the page directly at /customer-preferences I see this error from Next.js
So the issue only seems to happen when trying to make the API calls from the server and not the client.
I've also setup a simple express server to use instead, but not sure if this is necessary?!
const express = require('express');
const next = require('next');
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.all('*', (req, res) => {
return handle(req, res);
});
server.listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
});
When checking the server logs I get this:
FetchError: request to http://xxx failed, reason: getaddrinfo EAI_AGAIN xxx xxx:80
Any help would be greatly appreciated.
No, the server setup is not necessary.
This is happening because the browser/client is not capable of resolving your docker container's hostname. As it stands, the only solution I know of is to check for the req object in getInitialProps (so as to determine which environment the fetch will run in) and call the Docker hostname when on server, localhost when on client. E.g.
async getInitialProps (ctx) {
if (ctx.req) // ctx.req exists server-side only
{
// call docker-host:port
}
else {
// call localhost:port
}
}
My suspicion has to do with the fact that fetch is not a native node module but a client in browsers. So, if you navigate from one page to this page; per the documentation; getInitialProps will be called from the client side, making the fetch method accessible. A refresh ensures that the getInitialProps called from the server side.
You can test this theory by running typeof fetch from a browser's inspector and from a node REPL.
You are better of calling the method from component or using a third-party HTTP client like axios...
If you want to skip calling the AJAX method from the backend and only call it from the frontend, you can test if the method is calling from the frontend or the backend, like so:
CustomerPreferencesPage.getInitialProps = async () => {
if (typeof window === 'undefined') {
// this is being called from the backend, no need to show anything
return { initialData: null };
}
const res = await fetch(API_URL + '/preference-customer');
const initialData = await res.json();
return { initialData };
};

How to install SSL for the following setup (React Frontend + Nodejs Backend + Custom Domain Heroku)

General information about my setup
Currently I am building a web application using react and a nodejs API that is providing the data for this web application. Both apps are hosted on heroku.com and run independently from each other. I have bought a custom domain from a different hosting provider and used the heroku custom domain option to point the DNS to my website.
Technical details about my setup
NodeJS server: Express
NodeJS version: v10.15.0
React version: v16.2.0
Custom domain: www.tabbs.nl
Heroku domain: tabbs-web-app.herokuapp.com
The issue I am experiencing
I have been digging into a lot of documentation and tutorials in order to setup SSL for react / NodeJS but couldn't find a decent tutorial about how to set SSL / security for my setup.
Tutorials I already have read:
Node + Express + Lets Encrypt
How to use SSL/TLS with nodejs
Stack overflow posts and probably a whole lot more I am forgetting right now.
What do I want to achieve?
The goal I would like to achieve is setting up a secure connection between React web application (frontend) and NodeJS API (backend) so that all data between those is encrypted and safe. Also I want my custom domain (bought by a different hosting provider than Heroku) to be secure and forced using https.
For any questions or additional information please do not hesitate to ask!
Have you tried using the https module in node?
You can do something like this:
var express = require('express');
var https = require('https');
var http = require('http');
var app = express();
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
The app returned by express() is in fact a JavaScript Function, designed to be passed to Node’s HTTP servers as a callback to handle requests. This makes it easy to provide both HTTP and HTTPS versions of your app with the same code base, as the app does not inherit from these (it is simply a callback.
If you are using create react app, open your terminal and type “npm run build”. This creates a build folder with all of your static files.
Now go back to your node backend service and add the following:
var express = require('express');
var path = require('path');
var https = require('https');
var http = require('http');
var app = express();
const options = {
key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
cert: fs.readFileSync("/srv/www/keys/chain.pem")
};
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
If you’re using react router to handle routing for you web app then you would amend the GET request as such:
var express = require('express');
const path = require('path');
var https = require('https');
var http = require('http');
var app = express();
const options = {
key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
cert: fs.readFileSync("/srv/www/keys/chain.pem")
};
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
This ain't a complex issue, do not worry about ssl, just create your own certificate for Node.JS/Express and that's enough.
and, React has a built-in way of doing api calls,
add this line to package.json of your React installation,
"proxy": "http://localhost:8000/"
and just call the api service like this,
//Generic API Call
callApi = async () => {
const response = await fetch('/api/hello');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
// A Submit handler to proxy
handleSubmit = async e => {
e.preventDefault();
const response = await fetch('/api/myrequest', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ post: this.state.post }),
});
const body = await response.text();
this.setState({ responseToPost: body });
};
it all works.

Resources