I'm new to Server-Side Rendering and I'm wondering how do I apply best practices, separation of concern and naming convention for SSR, CSR and REST API code. Currently my folder structure looks like this:
.
├── client
│ ├── src
│ └── webpack.config.js - // This is the config for CSR
├── routes
│ ├── api
│ │ ├── controllers
│ │ │ ├── users.controller.js
│ │ └── routing
│ │ └── users.route.js
│ └── dao
│ └── usersDAO.js
├── app.js
├── server.js
├── webpack.client.js
└── webpack.server.js - // This one uses the app.js as an entry point
The REST API is implemented in the /server.js, the SSR routes are in my /app.js, and in my /server.js I have a conditional statements that uses environment variable and webpack's global constants to determine whether I'm using SSR or CSR, if so, server.js will then not listen on the server nor connect to the database, and instead just initialize the routes and the listen method and database connection are instead invoked in the /app.js rather than server.js. Is this a good practice or is there any better approach for separating these routes? Should I also make a folder and separate out each SSR routes? Also, how exactly does the folder structure of the separation of controllers, routes and DAO would look like?
Related
We are using Google Cloud Functions quite a bit (around 40 functions currently deployed). They are all in one repository, which is a monorepo for our nodejs backend. They are deployed using Github Actions when a new features/bugfix is merged. Our problem is that all functions are deployed, we dealt with concurrency to deploy more than one function at a time (multiple deploys are run in parallel) but we hit a wall. We are hitting the write quota (which is 80 requests pre 100 seconds) and we are not sure why. It seems that a single function deploy sends around 40 write requests which is isane and while deploying the functions in a slower manner (2 at a time max) it's not acceptable as the deploy would then take 40+ minutes.
While searching info about the quota I found that a single function deploy should do 1 write request (makes sense), but it does multiple for us and I couldn't find any way to debug this.
Example command used for deploying:
gcloud functions deploy functionName --runtime nodejs10 --memory=2048MB --timeout=540s --set-env-vars FN_NAME=functionName --trigger-http --allow-unauthenticated --project our-project --set-env-vars APP_ENV=production
Our functions structure looks like this (names have been replaced):
functions/src
├── fns
│ │
│ ├── atlas
│ │ ├── some-function.fn.ts
│ │ ├── some-function.fn.ts
│ │ ├── newsletter
│ │ │ ├── some-function.fn.ts
│ │ │ └── some-function.fn.ts
│ │ ├── suggestion
│ │ │ ├── some-function.fn.ts
│ │ │ └── some-function.fn.ts
│ │ ├── some-function.fn.ts
│ │ └── some-function.fn.ts
│ │
│ ├── leads
│ │ ├── some-function.fn.ts
│ │ ├── some-function.fn.ts
│ │ ├── some-function.fn.ts
│ │ ├── some-function.fn.ts
│ │ ├── some-function.fn.ts
│ │ └── some-function.fn.ts
│ │
│ ├── utils
│ │ ├── some-function.fn.ts
│ │ ├── some-function.fn.ts
│ │ ├── some-function.fn.ts
│ │ └── some-function.fn.ts
│ └── development.fn.ts
├── utils
│ ├── some-file-used-by-multiple-functions.ts
│ └── some-file-used-by-multiple-functions.ts
└── index.ts
development.fn.ts contains code which is run only on local machine and is ignored during deploy. It basically starts all the functions.
Every .fn.ts exports a single variable named after the function, which is simply a function handling the request request. This is wrapped in our "bootstrap" which handles connecting to databse, PubSub client and others.
index.ts is the entry file for Google Cloud with this content:
import { fns, getFnDefinition } from './bootstrap/get-fns';
// should export util
const ENV_FUNCTION_NAME = process.env.FN_NAME;
const shouldExportFn = (fnName: string) => {
if (!ENV_FUNCTION_NAME) {
return true;
}
return ENV_FUNCTION_NAME === fnName;
};
// export cycle
for (const fn of fns) {
if (shouldExportFn(fn.name)) {
const fnDefinition = getFnDefinition(fn);
exports[fn.name] = fnDefinition.handler;
}
}
export default exports;
Where fns is an array of { name, absolutePath } for our functions. It's read from filesystem (so no imports) and getFnDefinition requires the file and based on the result (exportet object) decides whether the function is triggered by HTTP request or PubSub message.
Also I saw the --entry-point=ENTRY_POINT option, but I'm not sure if that would solve our problem. Would it help if every function had its own entry point instead of the index.js?
The issue is how you are deploying them. You have all 40 functions in one Github repo, but how are you deploying them when one function requires a change? Do you resync / redeploy the whole thing? That would explain the 40 writes since you have 40 functions. I would recommend having them in individual repo or make sure each individual update doesn't cause all the functions to get updated.
Running into this too, but with READS! I hit 150k READS in a few hours just deploying (about 24 functions) about two dozen times. Looks like I have to even optimize my deployment strategy...
Our current solution is kind of dumb, but it works.
So it turns out the limit we were hitting (write quota) can be easily bypassed. What we do now is create a zip of the functions deploy, upload it to gcloud storage and then pass it as a parameter during deploy. This means we now don't reach the write quota (as no files are being uploaded) and everything works. We will however need to solve this in a better way in the future, as there is a limit of 60 when deploying functions and we currently have 48.
You can create controller for each route file to better manage your code. The routes have it's own folder in the root directory of a express project but where does the controllers belong?
I have seen that they have an own folder named "controllers" in the root directory too but I have also seen that each router has his own folder where router.js and index.js (the controller) are.
But what is the right folder structure for controllers for express js projects?
Ohk, just don't get too much confused. Organizing the files and folders totally depends on you, Express or node doesn't force you to maintain specific structure. In general developers like to keep things like below:
.
├── config # App configuration files
│ ├── sequalize.json # Sequalize config
│ ├── serviceOne.json # ServiceOne config
│ └── ... # Other configurations
├── routes
│ ├── controllers # Request managers
│ ├── middlewares # Request middlewares
│ └── routes.js # Define routes and middlewares here
├── services # External services implementation
│ ├── serviceOne
│ └── serviceTwo
│ └── ... # Other services
├── db # Data access stuff (Sequalize mostly)
│ ├── models # Models
│ ├── migrations # Migrations
│ ├── seeds # Seeds
│ └── index.js # Sequalize instantiation
├── core # Business logic implementation
│ ├── accounts.js
│ ├── sales.js
│ ├── comments.js
│ └── ... # Other business logic implementations
├── utils # Util libs (formats, validation, etc)
├── tests # Testing
├── scripts # Standalone scripts for dev uses
├── pm2.js # pm2 init
├── shipitfile.js # deployment automation file
├── package.json
├── README.md
└── app.js # App starting point
For more details you can refer https://medium.com/codebase/structure-of-a-nodejs-api-project-cdecb46ef3f8
If you have an exigence to implement your server-side project with Express js follow the answer below , or I suggest for you Nest.js
Nest (NestJS) is a framework for building efficient, scalable Node.js™ server-side applications
It has his own structure (services, controllers, routers ) well organized, and implementing good practices for server-side projects, take a look
It all depends on what you want, express don't have a "you need to use this format or else your api will surely fail" rule.
If you have an experience with using a PHP framework before, you can follow their MVC project structure if you want to or you can follow this nodejs best practices.
In the ssr vue (server side rendering for vuejs) documentation there's a code structure example containing a webpack build step:
https://ssr.vuejs.org/en/structure.html
The structure looks like this:
src
├── components
│ ├── Foo.vue
│ ├── Bar.vue
│ └── Baz.vue
├── App.vue
├── app.js # universal entry
├── entry-client.js # runs in browser only
└── entry-server.js # runs on server only
I am missing a main template(in rails or expressjs lingo a layout) which is supposed to contain the
<!--vue-ssr-outlet-->
marker.
Or am I missing something?
In SSR there is not a layouts folder. Your template (default) is in your App.vue file.
I am new to Node.js application development with expressjs framework.
I created a skeleton with expressjs-generator.
This skeleton have following directories and files:
.
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade
After it: I use the following command to run this application.
set debug=myapp:* & start npm
Now this is successfully running at Port 3000
This shows the homepage with Express Welcome message.
I want to make change in Homepage of my application. How it can be possible?
You can do that by changing the index.jade as #brute_force mentioned. If you are not familiar with jade, you can also add a index.html in the public folder and update the index.html instead.
From Deploying Yesod web app, I could copy the executable, static, and config file to run Yesod as a standalone web server. This is the directory structure.
├── config
│ ├── client_session_key.aes
│ ├── favicon.ico
│ ├── keter.yml
│ ├── models
│ ├── robots.txt
│ ├── routes
│ ├── settings.yml
│ ├── sqlite.yml
│ └── test-settings.yml
├── my-project
├── my-project.sqlite3
└── static
I tried to use different port by changing config/settings.yml
port: "_env:PORT:3002"
approot: "_env:APPROOT:http://localhost:3002"
However, the app is still accessible with port 3000. Even I removed the settings.yml, but there was no error message.
The way to change the port was from changing the environment variables export PORT=3002 from https://github.com/yesodweb/yesod/wiki/Configuration#overriding-configuration-values-with-environment-variables, but I it's not what I can use.
For development I can use stack exec -- yesod devel -p 3003 (how can I change the settings (e.g., port) used by "yesod devel"?), but I don't seem to use the -p option with the Yesod executable (my-project).
Why the executable does not (seem to) load config/settings? How to setup to use different port in Yesod web app?
It looks like that the setting file should be in the same directory as the executable.
I copied config/settings.yml in current directory, then run the ./my_project settings.yml, now it uses the 3002 port correctly.
I have some hints from https://github.com/yesodweb/yesod/issues/474