React + backend - project structure when sharing code - javascript

I really like the folder structure as can be seen here when dealing with a React frontend and a some backend with express:
root
├── backend
| ├── node_modules
| ├── public
| ├── src
│ │ └── Server.ts
| ├── package.json
| └── tsconfig.json
├── frontend (created using create-react-app)
| ├── node_modules
| ├── public
| ├── src
│ │ └── Index.js
| ├── package.json
| └── tsconfig.json
I think that having separate packages with individual node_modules is reasonable since the frontend and backend are basically completely different things, e. g. they need different node modules. Also, this modular approach is visually appealing to me and the repository looks tidy.
However, I encounter a problem with this structure when I need to share content between the frontend and the backend. I added a shared folder under the root-of-project which contains its own project with its own tsconfig.json, package.json and so on. This approach is a mix of the approaches here and here. For the backend, this works totally fine: having set up the tsconfig.json appropriately (using TypeScript Project References and aliased imports), I can reference the file root/shared/src/myFile.ts like this:
import { myFunction } from #shared/myFile;
I created the React frontend using create-react-app. It's ok for me that alias imports don't work, so I would have to use (inside the src folder in frontend):
import { myFunction } from '../../shared/src/myFile';
Sadly, these imports from outside the src directory are not supported by create-react-app and I don't want to use eject since I have no experience with webpack and don't want to maintain all the configuration files on my own (that's why I used create-react-app in the first place).
I know I can move the shared content to the frontend's src directory. But this would mean, I had to add the tags needed for using Project References in TypeScript, e. g. setting composite to true, in the frontend's tsconfig.json which seems odd to me and feels more like a hack. I'd like to have a separate npm project with my shared content.
Since create-react-app does not inherently support imports from outside the src directory, I thought that maybe I'm getting the big picture wrong. Isn't the folder structure I use right now a valid way of how to setup a React project with a backend? What mechanism does create-react-app provide to link files between the frontend and the backend? I could also think of having a root project with a src folder and inside of that the two folders backend and frontend. But this means, that we'd have one shared node_modules folder in root.
It's my first project with React and I'd love to get to know some best practicese for this kind of architectural problem. Some links to trustful resources where project structures for full-stack React development are explained would be really helpful. Thank you 😊

It's perfectly reasonable to want to share code between your front and back end. It's one of the reasons to code in javascript instead of Ruby or PHP.
You can accomplish exactly what you want by using yarn instead of npm and yarn workspaces: https://yarnpkg.com/lang/en/docs/workspaces/. At the top level you set up three modules/packages in your package.json (make sure you name the workspaces correctly in their respective package.json files):
"workspaces": {
"packages": [
"backend",
"frontend",
"shared"
]
},
Once you do, you can import shared code in your CRA app or your back end simply like this:
import { myFunction } from 'shared/src/myFile';
The drawback is that you can't import react components from the shared directory into frontend as long as you are using CRA. This won't affect you now since you only have one react app. Should you need to share react components among multiple projects, look into some on the suggestions above like bit.dev.
ADDENDUM!!! It's now possible to use CRA and yarn workspaces to share React code, if you replace CRA with CRACO. All you do is create another workspace with the shared react code. Then create a symbolic link in each module where you want to access it:
root
├── fontend-one
| ├── symbolic link to frontend-shared
├── frontend-two
| ├── symbolic link to frontend-shared
├── frontend-shared
Each of the CRA front end modules modules also requires a craco.config.js file where you tell web-pack not to follow symbolic links:
module.exports = {
// ...
webpack: {
configure: {
resolve: {
symlinks: false
}
}
}
};
You import from shared-frontend normally:
import { myFunction } from 'shared-frontend/src/myFile';
It's a pretty lo-fi solution but has proven robust for the year we've been using it.

Architecture is a tricky one, everyone has a different opinion and every option has pro and cons.
Personally I believe its best to separate the backend and frontend into sperate projects and keep them that way. Now as JavaScript/React/Node encourage component-based approaches a really nice way of sharing code between them is Bit.dev.
https://bit.dev
I am currently using it to share components and functions between three web apps and a few Node microservices.
A good structure for React app can be found here, this one works well and scales nicely:
https://hackernoon.com/fractal-a-react-app-structure-for-infinite-scale-4dab943092af
As for Express, there are so many ways to structure the project but personally recommend a folder for your Routes, a folder for your Controllers, this is where the logic for Routes live. Then go from there. Check this link out:
https://www.freecodecamp.org/news/how-to-write-a-production-ready-node-and-express-app-f214f0b17d8c/
Depending on what your building you may not even need a full backend you can check out the JAMStack here for more info:
https://jamstack.org
I would consider separating them though as the project scales it makes it much easier to manage. You can release your front end on something like Netlify and then use something like AWS or Azure to host your Node/Express server.

Having separate sub-projects for frontend and backend makes perfect sense due to vastly different dependencies. Mixing it up increases disk space consumption in production deployments and goes against security guidelines. Your folder structure is reasonable (except for public/ subfolders I'm unsure about, maybe I'm missing something).
The approach import { myFunction } from #shared/myFile; is fine. Just don't use CRA.
For a project with a single frontend and a single backend there is no need for a shared\ top-level folder because the frontend is the only source of 'UI truth' (e.g. the source of types and components related to UI) that is consumed by the frontend and the backend is the only source of 'API truth' consumed by both frontend and backend. With this arrangement only #backend/api_shared_stuff needs to be shared.
Some links to trustful resources where project structures for full-stack React development are explained would be really helpful. On the one hand, usually project authors have to explain plenty of other things and on the other hand keep the documentation (typically a README) reasonably concise. You may find that providing a justification/explanation why the subdirectory structure is this and not that was not the top priority.

Related

Vue 2 with Vue CLI - How to make src/static static so I can use images that are in there?

I'm trying to package my Vue CLI app to package src/static/xyz.png such that I can reference it as /static/xyz.png.
The Vue docs don't really show how to do this. I need a vue.config.js but between here:
https://cli.vuejs.org/guide/webpack.html
and here:
https://cli.vuejs.org/guide/html-and-static-assets.html#html
I can't figure out what I need to do. Any ideas? I've tried lots of webpack stuff but don't know webpack well enough, evidently...
In Vue CLI projects, files under public/ are automatically bundled as static assets, so you don't need a custom config for this. Your image should be stored in:
<projectRoot>/public/static/xyz.png
Then you'd reference them in your markup like this:
<img src="/static/xyz.png">
Note that these static assets are not run through Webpack, so they won't be optimized (i.e., the image file size might be unnecessarily large).

Share code between two React web projects

I have two different and separated web apps, both developed with React.
The two apps should share some React components.
I would like to have a general project structure as follow:
.
├── cms-client
├── my-app
└── shared
where:
cms-client and my-app are two standard React apps;
shared is a folder containing the shared components, that should be used by the other two apps.
I tried to add a symbolic link inside the two src folders of the apps, like:
ln -s ../../shared/ .
executed inside the src folder of each app.
In this case, when I try to use a shared component, the compilation failed:
../shared/Example.js
SyntaxError: /my-long-project-path/React/shared/Example.js: Unexpected token (6:12)
4 | render() {
5 | return (
> 6 | <div>
| ^
7 | <p>I am an Example of Shared Component</p>
8 | </div>
9 | )
like it is compiled as a standard js file, and not a React jsx file.
So, I'm trying a different approach, using a custom configuration of the jsconfig.json file. Now, my idea is to inject somehow the shared folder, but it seems impossible.
I could write a Gulp script that watch the shared folder, and then copy the contents inside the shared folder of the two project, but this isn't an optimal solution and very error prone (from my IDE, I need to pay attention on which of the three shared folder I'm editing). Moreover, the standard react-scripts is already watching the src folder for any changes. So, if someone has a better solution, it will be great!!!
Can you do npm link on your shared ?
This will compile, package, and copy to some location on you machine
Then on both cms-client and my-app do npm link shared
It will create symlink in node_modules point to the locally shared

Jetty to serve plain html and JS code

I've installed Jetty using homebrew on osx. Jetty is in the usual folder:
/usr/local/Cellar/jetty/9.4.5.v20170502
However, we are not going to use this for anything related to Java. It's just our requirement that the web server we will have in production is Websphere. Our code is pure html and some JS. Only static files. All the tutorials on the web require modifying XML files and setting up complex folder structures for java apps. We do not need any of this. All we want is to put html/js files in some specific place.
Is there an easy way to do this? Do we have to create a start.jar for this simple task? How do we create a WAR package via Maven or something else? Would appreciate any simple pointer to getting this dev environment set up. Thank you!
It seems you might be overthinking this a bit. start.jar is merely the JAR file that starts up the sever. You can easily host static content out of Jetty. I am unsure of your specific requirements, but for the sake of an example, I will just assume you have a simple webapp with a page and some resources. Your implementation might look like this.
Inside the Jetty 9.4.5.v20170502 directory - Also called Jetty_Home (you can learn about Jetty_Home vs Jetty_Base here), you'll find the following directories:
bin demo-base etc lib logs modules resources webapps
These are the standard of truth and should not be modified (though demo-base can be removed entirely if desired). You will want to create your own Jetty_Base directory to host your content out of, let's call it stackoverflow for this example. It is worth noting that a Jetty_Base can exist anywhere, I am just keeping it here for ease of this example.
Inside the stackoverflow directory you'll find nothing to start with, so we will need to enable some modules to populate our Jetty_Base. Now, I have no idea what all you plan to do with this webserver, so I will keep it minimal - enough to host the content. From inside this directory run:
> java -jar ../start.jar --create-startd
MKDIR : ${jetty.base}/start.d
INFO : Base directory was modified
This will create a start.d directory for all of our module files to live in, which makes modifying and configuring things a breeze (though for the sake of this example everything will be left default). Now we need to add the modules which will enable the functionality on the server. You can view a whole list of available modules by running:
> java -jar ../start.jar --list-modules
I am not going to paste the whole list here but instead enable the following:
> java -jar ../start.jar --add-to-start=server,client,deploy,http,webapp,jsp
INFO : webapp initialized in ${jetty.base}/start.d/webapp.ini
INFO : server initialized in ${jetty.base}/start.d/server.ini
INFO : security transitively enabled
INFO : apache-jsp transitively enabled
INFO : servlet transitively enabled
INFO : jsp initialized in ${jetty.base}/start.d/jsp.ini
INFO : jndi transitively enabled
INFO : client initialized in ${jetty.base}/start.d/client.ini
INFO : http initialized in ${jetty.base}/start.d/http.ini
INFO : annotations transitively enabled
INFO : plus transitively enabled
INFO : deploy initialized in ${jetty.base}/start.d/deploy.ini
MKDIR : ${jetty.base}/webapps
INFO : Base directory was modified
This will enable all the modules I defined (server,client,deploy,http,webapp,jsp) as well as any dependencies required for them to operate and any required folders (such as webapps).
Now, I created a very small webapp named example. I've gone ahead and moved it into the webapps directory inside our stackoverflow Jetty_Base:
> tree
.
├── start.d
│   ├── client.ini
│   ├── deploy.ini
│   ├── http.ini
│   ├── jsp.ini
│   ├── server.ini
│   └── webapp.ini
└── webapps
├── example
│   ├── images
│   │   ├── jetty-header.jpg
│   │   └── webtide_logo.jpg
│   ├── index.html
│   └── jetty.css
└── example.xml
I have a context xml file I created for this webapp named example.xml, which is only serving static content, and it is very simple:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="exampleApp" class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">/example</Set>
<Set name="war"><Property name="jetty.webapps" default="."/>/example</Set>
</Configure>
This configures the webapp so that when I run the server I can navigate to, in this case, localhost:8080/example and see my content. Now all I have to do is run my server. From inside the stackoverflow directory:
java -jar ../start.jar
2017-06-08 10:36:32.300:INFO::main: Logging initialized #427ms to org.eclipse.jetty.util.log.StdErrLog
2017-06-08 10:36:32.477:INFO:oejs.Server:main: jetty-9.4.5.v20170502
2017-06-08 10:36:32.494:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///Users/example/installs/repository/jetty-distribution-9.4.5.v20170502/stackoverflow/webapps/] at interval 1
2017-06-08 10:36:32.633:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=26ms
2017-06-08 10:36:32.668:INFO:oejs.session:main: DefaultSessionIdManager workerName=node0
2017-06-08 10:36:32.668:INFO:oejs.session:main: No SessionScavenger set, using defaults
2017-06-08 10:36:32.669:INFO:oejs.session:main: Scavenging every 600000ms
2017-06-08 10:36:32.692:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext#6e06451e{/example,file:///Users/example/installs/repository/jetty-distribution-9.4.5.v20170502/stackoverflow/webapps/example/,AVAILABLE}{/example}
2017-06-08 10:36:32.713:INFO:oejs.AbstractConnector:main: Started ServerConnector#21a947fe{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2017-06-08 10:36:32.714:INFO:oejs.Server:main: Started #840ms
And now you have a server up and running and content being served. I hope this helps. It can seem overwhelming at first, but it isn't so bad once your get your hands dirty. I recommend reading up more at the official documentation.

How to organize folders and files on Meteor project?

I'm trying to understand Meteor as I create a project and I find some things a little difficult to understand so far.
1- When they say I can create a server and a client folder, where exactly I am meant to do so? Sibling of .meteor ? And will everything be at client's or server's scope when the app starts or do I have to do something else? If I create a foo.js and a foo function inside it in client folder, can I just call foo() in Meteor.isClient and it will work?
2- I need to create an upload folder so people can upload their stuff (images). So where am I supposed to do this? Plus, how can I get the absolute path to my project and find this upload folder inside?
During my attempts I tried the following:
fs = Meteor.npmRequire('fs');
__ROOT_APP_PATH__ = fs.realpathSync('.');
But __ROOT_APP_PATH__ is .meteor\local\build\programs\server. Quite hidden right?!
3- I saw some people uploading and saving files on MongoDB directly. This is something we usually don't do with relational databases. We move the file to a known folder on a CDN or on our own disk and save the hash or name of that file so we can easily find it. Isn't it encouraged with Meteor + MongoDB? Why would I save the file itself on Mongo instead of moving it to a folder?
not any specific way to do but meteor recommend it doing this way
http://docs.meteor.com/#/basic/filestructure
FOLDER STRUCTURE:
both/ (OR lib/) -- common code for server and client
|- collections/ -- declare collections (e.g Employer = new Meteor.Collection("employer");)
|- router / -- router code(e.g Router.route(..))
client/ -- client side code
|- global/ -- all global variable for client
|- helpers/ -- global helper for client (for all templates)
|- plugins/ -- all the plugins code(if you use any)
|- stylesheets/ -- css / less files
|- templates/ -- all templates
|- home.html -- home template(html)
|- home.js -- home template(js)
public/ -- images/icons/fonts (meteor looking at this file)
server/ -- server code
|- methods/ -- server methods/API (e.g Meteor.methods({...}))
|- publish/ -- publish code from server
this is the basic folder structure for meteor project which i follow. For further reference or Documentation. For any question feel free ask in comments..

Excluding references from Typescript compilation?

I have a bit of a strange (but in my view sensible) scenario.
I have a web site, mobile application and maybe going forward a web server all written in Javascript. I have a huge chunk of functionality which is shared between all these systems. This shared stuff would be models, repositories, business logic etc etc.
If we exclude the web server bit as that is a future idea, the web application has a directory structure like this:
app
|- assets
|- models
|- services
|- migrations
|- view-models
|- views
|- descriptors
Now each of these applications is broken down into 2 areas, the core and ui sections, the core is the re-usable stuff such as models, services, migrations and the ui stuff is ONLY used for that application which would comprise of view-models, descriptors (Incase you are wondering views are all html and assets are css, images etc).
Before I was adopting typescript I had a build script which would basically combine all the core files together and minify them. Then it would combine all the UI ones together and minify them. That way in the mobile application I can then just use the my-app.core.min.js and everyone is happy, I am reusing all the re-usable components from the main web application. However I do not need the ui stuff as the mobile UI is completely different to the main web ui, and the web service would not even have a UI going forward.
SO!
With that context explained lets jump back to the Typescript problem at hand. Currently the typescript files are compiled by tsc.exe (version 0.83) via a build script, which just wraps the interaction.
So in the new Typescript world the structure now has a references folder like so:
app
|- assets
|- models
|- services
|- migrations
|- view-models
|- views
|- descriptors
|- references <- NEW ONE!
This references folder is automatically populated by the build script with all local references, so it will trawl through the whole directory tree find all typescript files and build a huge reference file, which is a file full of the reference declarations for local typescript file, to find out more about what im on about look over this question:
Can you create Typescript packages? like c# dlls
So now when I run the build script the following steps happen:
Compiling for Core
Find all *.ts files within the models, services, migrations folders and subfolders
Add all the previous files into an array and also add in the reference files
run tsc.exe with a command like so tsc.exe --out <something>.core.js <previous_file_list>
Compiling for UI
Find all *.ts files within the view-models, descriptors folders and subfolders
Add all the previous files into an array and also add in the reference files
run tsc.exe with a command like so tsc.exe --out <something>.ui.js <previous_file_list>
Now I was expecting this to output 2 files my-app.core.js which ONLY contained the core files, and a my-app.ui.js which ONLY contained the ui files. However they both include everything...
Now after thinking about this, it must be due to the references, as they are both referencing all files, however thats just a compilation dependency in my eyes, not something that needs to be compiled WITH the outputted javascript. In c#/java you would not expect a referenced dll/jar to be compiled into your outputted dll/jar, its just a runtime dependency which is required.
I have tried having 2 separate reference files, one for core and one for ui, but as the ui will depend on core I get same problem, although at least this way the my-app.core.js is devoid of any ui related guff.
So is there a way to have references but NOT have them be outputted into the generated javascript files?
You can accomplish this by generating definition files for your TypeScript files:
tsc --declaration FileName.ts
In your build script do this for each TypeScript file and use the generated FileName.d.ts as the reference instead of FileName.ts
I had the following files:
-BusinessObjects
--Product.ts
--Customer.ts
--Order.ts
--BusinessObjects.d.ts
BusinessObjects.d.ts looks like this:
/// <reference path="Customer.d.ts" />
/// <reference path="Order.d.ts" />
/// <reference path="Product.d.ts" />
with Product, Customer, and Order each have a reference to BusinessObjects.d.ts
when I run:
tsc --out combine.js Customer.ts Order.ts
The output only references Customer and Order, Product is not included. When I referenced the *.ts files directly in my BusinessObjects.d.ts file however the combined output did include the unwanted file.

Categories

Resources