Serverless NodeJS File/Folder Structure - node.js

I'm setting up my first project using serverless and while I'm finding a lot of great "getting started" tutorials, I'm having a hard time finding anything about actual project structure.
My thoughts is to use the below structure for my functions, shared libs and core configuration/dependencies:
.
├── functions/
│   │
│ ├── users/
│ │ ├── handler.js
│ │ └── serverless.yml
│ │
│ └── roles/
│ ├── handler.js
│ └── serverless.yml
│
├── shared/
│ └── common.js
│
├── node_modules/
└── package.json
My main curiosity is around deployment and how that pertains to dependencies and shared files. Additionally, automating deploy of this structure seems strange as I'm gathering I would need to deploy each of the functions separately which I can script, but wondering if that's needed or advised.

I have dealt with this a bit and found it quite frustrating. If you deploy from your setup, what does your api look like? With individual serverless.yaml files you end up with independent api endpoints (assuming you are triggering with api calls and not something like s3).
I ended up with a structure like this:
|- serverless/
|--- serverlsss.yml
|--- web pack.config.js
|--- dist/
|--- node_modules() /* dev and common code */
|--- src/
|----- function1/
|-------- node_modules
|-------- package.json
|-------- index.js
|----- function2/
|-------- node_modules
|-------- package.json
|-------- index.js
I use the serverless webpack plugin to output the the individual functions into the dist/ directory. The serverless.yaml then points to these.
The webpack.config.js looks like this:
const nodeExternals = require('webpack-node-externals');
const path = require('path');
module.exports = {
entry: {
'function1': './src/function1/index.js',
'function2': './src/function2/index.js',
},
target: 'node',
output:{
libraryTarget: 'commonjs2',
path: path.join(__dirname, './dist'),
filename: '[name].js'
},
externals: [nodeExternals()],
module: {
loaders: [
/* Babel is nice, but it's adding a some bulk and I don't need it
{
test: /\.js$/,
loaders: ['babel'],
include: __dirname,
exclude: /node_modules/,
}, */
{
test: /\.json$/,
loaders: ['json']}
],
},
};
// externals: [nodeExternals()],
// nodeExternals seems to break aws-sdk when serving locally
// aws-sdk should be in dev-dependencies of root folder
// that way it's available in dev but doesn't get packaged.
// It's available to the lambdas when deployed.
After that just make sure you set the individual flag in serverless.yml
package:
individually: true
The webpack plugin is quite nice and does most of the heavy lifting. With this I can do a single deploy and all the functions end up as individual lambda functions all under one api endpoint. You also get the Webpack dev server so you can run serverless webpack serve to test your functions.
It was a bit of a pain to setup, but it's been working pretty well.

Related

How can I set up Typescript to import types from another Prisma folder?

We're using Typescript, Prisma and TRPC in two NodeJS services that need to communicate with each other. Both services have their own database, so the generated Prisma client in node_modules is different between the folders. The folder structure is set up like so:
.
├── service_1/
│ ├── tsconfig.json
│ ├── package.json
│ └── ...rest of project
└── service_2/
├── tsconfig.json
├── package.json
└── ...rest of project
Because we're using TRPC, service_1 imports types directly from service_2 like the following example:
import type { AppRouter } from '../../../../service_2/src/routers/index'
This works fine in VSCode, however we want to put typechecking as an CI action on service_1, but running tsc -p ./tsconfig.json --noEmit in the root of service_1 shows a lot of errors like this:
../service_2/src/services/dog.ts:53:10 - error TS2339: Property 'dog' does not exist on type 'PrismaClient<PrismaClientOptions, never, RejectOnNotFound | RejectPerOperation | undefined>'.
53 prisma.dog.findMany({
We surmise that this is caused by Typescript using the generated #prisma/client package from the service_1 folder even when typechecking service_2, but we've tried changing a number of options in our tsconfig.json to no avail.
Our current tsconfig.json in service_1 looks like this:
{
"compilerOptions": {
"jsx": "react-jsx",
"strict": true,
"paths": {
"/*": ["./*"]
},
"baseUrl": "./",
"noEmit": true,
"esModuleInterop": true
},
"exclude": [
"**/node_modules/*",
"**/dist/*",
"**/.git/*",
],
}
Some of the options we've tried include:
Setting rootDir to ./
Setting rootDir to ../
Setting rootDirs to ["./", "../service_2"]
Adding #ts-ignore or #ts-nocheck above the type import
Adding service_2 to the exclude option
Is there a way to get typechecking across two projects like this with separate node_modules on one that imports from another?

ESLint Configuration File - indicated in cousin folder

I am trying to add an eslint configuration to my project. I do not want to add it in the root folder but in the .vscode one.
I have the following folder structure :
my-poney-project
├── .vscode
│ └── my-poney-project.code.workspace
│ └── .eslintrc.json
├── lib
│ └── source.js
└─┬ tests
└── test.js
and I want to lint file which are present in the lib and tests folder, with the eslint configuration in the .vscode folder.
Currently my my-poney-project.code.workspace file contains the following :
{
"folders": [{
"path": ".."
}],
"settings": {
"eslint.alwaysShowStatus": true,
"eslint.debug": true,
}
Do you know how could I indicate to eslint to use the .vscode\.eslintrc.json whereas it is not in a parent folder of the files I am trying to lint (lib and test)?
For example, is there a parameter to add in the my-poney-project.code.workspace ?
References which may be related to this question :
https://eslint.org/docs/user-guide/configuring/configuration-files#cascading-and-hierarchy
https://eslint.org/docs/user-guide/configuring/configuration-files#personal-configuration-files-deprecated
I would be grateful for any comments or responses,
Cheers !

For all subfolders in a Node.js project, use global custom entry point for require

Question
Is it possible to configure a global, custom entry point to be used by require for all subfolders in a Node.js project?
Rationale
When working in Node.js, I like having my index.js file as the topmost file in each subfolder in my IDE.
However, depending on the IDE and the way it sorts files, this is not always possible (for example, VSCode has several sorting options available, and none of them can achieve this).
To achieve that, I prefix it with _index.js, but then lose the built-in capability of require to recognize it as the default entry point.
Although this can be mitigated by adding a package.json into each subfolder, with a main property directing to the entry point file - I'd like to know if there's a way to define a "global" custom entry point, be it in the topmost package.json or using some npm package which I'm not aware of.
Example
Let's say I have the following folders structure, and assume that our IDE sorts files alphabetically:
MyApp
├── app.js
├── package.json
├─┬ featureA
│ ├── func1.featureA.js
│ ├── func2.featureA.js
│ └── index.js
└─┬ featureB
├── func1.featureB.js
├── func2.featureB.js
└── index.js
To keep index.js as the topmost file, we prefix it with an underscore, and use a package.json for each subfolder to define it as an entry point:
MyApp
├── app.js
├── package.json
├─┬ featureA
│ ├── _index.js
│ ├── func1.featureA.js
│ ├── func2.featureA.js
│ └── package.json
└─┬ featureB
├── _index.js
├── func1.featureB.js
├── func2.featureB.js
└── package.json
The package.json for both featureA and featureB is identical:
{
"main": "_index.js"
}
That package.json is necessary so that we can use require in the following way in app.js:
// app.js
const featureA = require('./featureA');
const featureB = require('./featureB');
But can these two package.json files be replaced with some "global" alternative?

Environment variables in parcel nested modules

I'm trying to use environment variables in "nested" dependencies of a project bundled with Parcel, using .env files, but I'm getting undefined instead of the desired value.
According to the docs, NODE_ENV is automatically set to "production" when building, else it's set to "development", so it will load .env.local either .env.production depending on that value.
Consider my file structure:
./
├── .env.local
├── .env.production
├── src
│ ├── index.html
│ ├── scripts
│ │ ├── main.js
│ │ └── APIService.js
└── package.json
My script for launching the app in package.json is this:
"scripts": {
"start": "parcel src/index.html"
}
… and the src/index.html file loads the main.js file with a simple tag:
<!DOCTYPE html>
<!-- ... -->
<script src="main.js" defer></script>
If I try to log an environment variable set in .env.local in main.js, it will work perfectly because it is the JS entry point, but if I try to get the same exact variable into an imported module like APIService.js, I get undefined:
// main.js
import APIService from "./APIService";
console.log(process.env.API_ENDPOINT); // ✅ http://localhost:5001/functions/app
// APIService.js
console.log(process.env.API_ENDPOINT); // ❌ undefined
Am I missing something?
How to get access to the environment variables inside imported files?
PS: I've already tried to use the dotenv package in addition, but without success.

How can I use jasmine with a server-side typescript project?

I have a project that contains modules for both the server and client of my application, which are each built using webpack. I'm using Karma with Jasmine to test my client (which uses Angular) and I would like to use Jasmine to test the server, which is written in typescript, too.
Unfortunately, the only guides that I could find online used jasmine-node (which to be untouched for the past few years) instead of jasmine-npm. Can anyone suggest a way that I can use Jasmine, or an alternative, for testing within my project?
I've tried writing a jasmine.json file, or editing the one generated by jasmine with the init cli command, however this doesn't seem to work with typescript files.
At the moment, my project's structure is like so:
├── client
│ ├── karma.conf.js
│ ├── protractor.conf.js
│ ├── src
│ ├── tsconfig.json
│ └── webpack.config.js
├── server
│ ├── src
│ ├── tsconfig.json
│ └── webpack.config.js
└── node_modules
Its definately possible to use jasmine for your server side tests. Follow these steps and you'll be fine:
1) In your package.json add the following dev dependencies:
"jasmine": "latest",
"jasmine-core": "latest",
2) Setup your jasmine.json so that it will include all files you want to run tests on, etc.
{
"spec_dir": "dist/dev/server/spec",
"spec_files": [
"unit/server/**/*[sS]pec.js"
],
"helpers": []
}
3) Add unit.ts that will bootstrap your testing process:
const Jasmine = require("jasmine");
let j = new Jasmine();
j.loadConfigFile("./jasmine.json");
j.configureDefaultReporter({
showColors: true
});
j.execute();
Now all you have to do is compile and run your resulting unit.js in nodejs.

Resources