First things first, version of various dependencies
Ubuntu - 15.04
NodeJS - 6.10.3
NPM - 3.10.10
Sails - 0.12.13
Second, here's what I did:
1. Installed Sails globally
2. In the directory /var/www, ran the command sails new app
3. Created a file UserController.js in api/controllers/v1
4. Created a file User.js in api/models
Code for UserController.js
module.exports = {
findOne: function(req, res) {
return res.send("Hello World!!! User -> findOne");
},
login: function(req, res) {
return res.send("Hello World!!! User -> login");
}
}
Code for User.js
module.exports = {}
Now, when I start my server using sails lift, here's what happens:
Browse http://localhost:1337/v1/user/1 - Page Not Found (404)
Browse http://localhost:1337/v1/user/login
Hello World!!! User -> Login
I know I'm going to sound silly, but I thought it is probably because I didn't use the Generator feature of Sails. So, here's what I did next: sails generate api v1/Product. And the file structure after this command is like this:
api
|-- controllers
|-- v1
|-- UserController.js
|-- V1
|-- ProductController.js
|-- models
|-- User.js
|-- V1
|-- Product.js
I wrote similar code in ProductController.js as I did in UserController.js and I expected that now the Product API should work but the result was same as in case of /v1/user/1.
As per the SailJS Blueprint API, this should have worked. So, can anyone explain why this is happening and how can I make /v1/user/1 and /v1/user/login both work as expected.
You have a couple of ways to achieve this. The first option is to set the restPrefix to /v1 inside config/blueprints.js
An optional mount path for all REST blueprint routes on a controller and it does not include actions and shortcuts routes.
This allows you to take advantage of REST blueprint routing even if you need to namespace your RESTful API methods
Now with same directory structure i.e., /controllers/v1/UserController.js and /models/User.js, you should be able to access both the /login and findOne methods.
The second option is to set the prefix to /v1 inside config/blueprints.js
An optional mount path for all blueprint routes on a controller, including rest, actions, and shortcuts.
This allows you to take advantage of blueprint routing, even if you need to namespace your API methods.
(NOTE: This only applies to blueprint autoroutes, not manual routes from sails.config.routes)
Now the directory structure would be /controllers/UserController.js and /models/User.js. You have the mapping /v1 setup for you.
On the other hand, you can completely turn off blueprint routes and set up your own routes inside /config/routes.js. This would give you more flexibility and you can also have environment specific routes inside config/env/[your_env].js
Related
Im working on a Vue frontend that consumes a backend API that are both being deployed to the same kubernetes cluster. I would like to make the vue frontend application configurable so I can assign the address to the backend service on container startup instead of during build time.
I've been trying to do this by following Hendriks answer to this thread:
Pass environment variable into a Vue App at runtime
Due to my lack of Node and Javascript experience, I do not understand the following:
Where in the filestructure the files should be placed?
Why the config.js describes a function, and not just as export default { configObj: var } an object with the variables?
How I can access the variable in config.js through /config.js in the rest of my app? I can see the file in my browser at /config.js, but imports does not work.
I've currently placed the files like so:
/app
/public
index.html << I put the script tag in the head of this file.
...
/src
/config
config.js
main.js << setting axios.defaults.baseURL here. import config from '#/config/config.js' results in the str 'config.js'.
...
vue.config.js
Even though I know my backend service IPs/addresses are not supposed to change in production, setting these values before build seems like a very static and manual way to do it. During development running the app and backends locally on minikube, waiting for long builds quickly becomes very time consuming.
Very greatful for any insight in how I can achieve this, or any insight into why I can't seem to get Hendriks proposal to work.
It's important to note that what you are trying to do doesn't have anything to do with node. The execution environment of your Vue app is going to be the browser and not node (even if you are serving it with node). This is also why you can't do export default of your config, as some browsers won't understand that.
But here is a method you can use in order to get the config to your app using k8s.
Create a k8s config map, which will contain your config.js, something like:
apiVersion: v1
kind: ConfigMap
metadata:
name: vue-config
data:
config.js: |
function config () {
return { serverAddress: "https://example.com/api" };
}
Then in your pod/deployment embed the file:
apiVersion: v1
kind: Pod
spec:
containers:
- name: your-vue-app
image: your-vue-app:1.0.0
volumeMounts:
- name: config-volume
mountPath: /app/public/config.js
volumes:
- name: config-volume
configMap:
name: vue-config
Note that you are putting the config in the public directory. After that you can add the script tag in the index, which loads the config.js. Having the config as a function is useful, because you ensure some level of immutability, also I think it looks a bit better that a global config var.
When you want to use the configuration, you just call the config function anywhere in your configuration:
const conf = config();
I have a directory structure where the index.js file is located at:
path/to/home-page/src/routes
Note this is also the __dirname. And the html file is located at:
path/to/home-page/public/bootstrap/Homepage/page.html
I am serving the html document by relative path via:
const html_rel_dir : String = "../../public/bootstrap/Homepage/"
...
index.get('/homepage', (req, res, next) => {
var path_to_html = path.join(__dirname, html_rel_dir, 'homepage.html')
res.sendFile( path_to_html );
});
This smells "bad" to me in that the code will break if the directory structure changes, what is a better way to specify this?
Addendum, my app is configured so that public files are served via:
`this.app.use(express.static(path.join(__dirname, "public")));`
Using a View/Template engine can abstract the directory structure by letting you call out your view folder once at the top level. I use Nunjucks but its the same idea with the others. Handlebars, Mustache, etc. So my actual route calls look like this.
general-pages.routes.js
router.get('/feedback', function (req, res) {
var data = getSomeData()
res.render('general/feedback.html',data)
})
The framework early on in my main application was told the location of my views so from that point on it assumes any path is a subdirectory of my primary view folder. The one handler you see above handles lots of general pages. Then its referenced in my main app.js or whatever you call your main file like so:
app.use('/general',generalRoute)
So anything for /general goes to the generalRoute handler general-pages.routes.js defined earlier. The full URL to the user is /general/feedback
How does it know how to find my views?
nunjucks.configure('views', {
autoescape: true,
express : app
});
The code above says start looking for the folder called "views" inside the project folder. Its smart enough to use the internal node environment variables to determine the root directory. If you choose any of the other template engines they offer their own options for doing this.
I've got one incredibly long index.js route file for my node/mongo/express application. Here is the structure.
app.js
/models
Schedule.js
Task.js
etc...
/routes
index.js
/public
/javascript
etc...
I want something like this
app.js
/models
Schedule.js
Task.js
etc...
/routes
index.js
schedule.js
tasks.js
etc...
/public
/javascript
etc...
How can I split up my index.js file?
For example, I have tried to do something like this in my app.js file
var routes = require('./routes/index');
var tasks = require('./routes/tasks');
// later in the file
app.use('/', routes);
app.use('/tasks', tasks);
However, it is not finding my task routes like it did before when I had only the index.js. This is probably because I took all the routes starting with "/task" from my routes/index.js and placed them in routes/task.js, but why express not recognizing the routes in this new file?
Any advice at all would be helpful, my friend and I are very new to the whole meanjs way of doing things, and basically cobbled together what we have from various tutorials. Now we desperately need to refactor, because routes/index.js is getting too long to work with. I did my best to do what makes sense intuitively, could anyone out there give advice on how to break up our routes file?
Well, the answer to this question lies in how you import (require in this context) the router instance. Each one of those files should do a module.exports with a router object or something similar where the routes can be mounted on the app instance you create.
For example, have a look at how I do it (with yeoman's help that is) in a project I have on GitHub here, then refer to how the router object is exported for each route like this example.
As another example of doing this, here is an open source project I've been contributing to here. Notice that we have a slightly similar approach here, then have a look at an example route declaration (with the corresponding export) here.
I can't find a good bare minimum example where I can wire up an express.js route to call a react view.
So far this is what I have.
+-- app.js
+-- config
| +-- server.js
+-- routes
| +-- index.js
+-- views
| +-- index.html
app.js
require('./config/server.js');
require('./routes/index.js');
config | server.js
"use strict";
var express = require('express'),
app = express(),
routes = require('./routes');
app.set('view engine', 'html');
app.engine('html', ); // how do I tell express that JSX is my template view engine?
var port = process.env.PORT || 3000;
app.engine('handlebars', exphbs({ defaultLayout: 'main'}));
app.set('view engine', 'handlebars');
var server = app.listen(port, function(){
console.log('Accepting connections on port ' + port + '...');
});
routes | index.js
app.get('/', function(request, response){
// how do we call the route that will run index.html ?
request.render('index');
});
views | index.html
<!DOCTYPE html>
<html>
<head>
<script src="build/react.js"></script>
<script src="build/JSXTransformer.js"></script>
<script type="text/jsx" src="build/noEpisodes.js"></script>
</head>
<body>
<div id="noEpisodesMessage"></div>
</body>
</html>
and then my index.htm page as well as generated jsx.
2021 edit:
the modern solution is to use the create-react-app tool, followed by dropping in your code where needed. It's been set up to do the rest for you. The React landscape has changed a fair bit over the last six years.
original 2015 answer:
The usual thing is to use react-router to take care of the routes; write your React app as a single React app (i.e. all your JSX source ends up bundled into a single app.js file, which knows how to load all your "pages" or "views") and then use express (or Hapi, or any other server process) mostly as your API and asset server, not your page/view generator.
You can then tap into the routes you set up in the react-router Router object so that on the express side you can forward your users to the URL that react-router can deal with for content loading, so you get
user request site.com/lol/monkeys
express redirects to /#/lol/monkeys
your react app loads the correct view because of the routes in Router
optionally, your app does a history.replaceState so that the user sees site.com/lol/monkeys (there are some react-router tricks to do this for you)
You can also automate most of this through server-side-rendering but the name can be confusing: you still write your React app as if there is no server involved at all, and then rely on React's render mechanism to fully render individual "pages" for a requested URL which will show all the right initial content while also then loading your app and silently hooking it back into the content the user is looking at, so that any interactions past the initial page load are handled by React again, and subsequent navigation is "fake" navigation (your url bar will show a new URL but no actual network navigation will happen, React simply swaps content in/out).
A good example for this is https://github.com/mhart/react-server-example
The other answers work with the usual way to use react, to replace an element in the dom like so
React.render(<APP />, document);
but if you want react to be your "template language" in express, you can also use react to render a simple string of html like so
app.get('/', function(req, res) {
var markup = React.renderToString(<APP />); // <-- render the APP here
res.send(markup); // <-- response with the component
});
there are a few other things you need to take care of in terms of bundling all the dependencies and working with jsx. this simple minimalist tutorial and this blog post helped me understand the situation better
note that the sample code in the tutorial uses this syntax
var markup = React.renderToString(APP());
which has been deprecated and will cause an error. to use it you'd have to replace
var APP = require('./app');
with
var APP = React.createFactory(require('./app'));
or just render jsx like i did in the first example. to get the tutorial to work i might have also had to use more recent versions of the dependencies in package.json.
once you've got that down a fancier tutorial shows a more powerful way to use react-engine to render react within express
If you want to keep it simple you can do this for the entry point of your app:
routes | index.js
app.get('/', function(request, response){
// how do we call the route that will run index.html ?
res.sendfile('../views/index.html');
});
React is going to target a specific element on your index.html page in it's render method:
React.render(<Application />, document.getElementById('foo'));
So if your javascript is included in index.html and an element with that id is present react is going to inject your Application into that div. From that point onwards you can do all of the routing with react-router OR you can setup different routes in express and handle them like you did index.html
Instead of manually dealing with React.render you can use a library called react-helper (https://github.com/tswayne/react-helper). It sets up a div for you, binds your react components to the div, allows you to pass properties to your components server side, and can even handle server-side rendering if you have webpack configured for your node app or are willing to use babel-register (not recommended for prod).
I'm now learning Node.js and Express, and want to use mysql module to render multiple unique pages, so instead of writing out var connection=mysql.createConnection({user:'root', password: 'root'...etc}) line on every file located under routes directory, I'm sure it's better off to just call my own mysql config file on each routing file.
However, where should I put the config file on Express hierarchy and how can I call the file from within each routing file? I know all images, style sheets, and javascript files should be located within each specific directory under public directory, but I don't know where to put all the other files that are intended to be accessed from routing files.
I also want to know whether all of my files, ranging from main app.js to files under routes directory to files under public directory, etc... can be found by users once I publicize this web application on the Web. If it's the case, then I think I should not put database config file on directories that clients can access to...right? In other words, I want to make sure which files can be accessed to by clients and which cannot in order to avoid security attacks.
To answer your first question "Where to put the config file?":
This is a little bit personnal. Put it to the root of your application.
config.js:
module.exports = {
database:{
host: ""
user: "..."
}
}
then you include it in you app.js:
app.js:
...
config = require("./config");
db = config.database;
var connection=mysql.createConnection({user:db.user, ...})
Note that you might want two config file, one in you version control and one private to the machine. Different developers might have different database access for example. But I don't think you have to worry about that for now.
For your second question "Are all my files public?":
No, only the file you specify as static (with the express middleware) will be served.
app.js:
...
// Exposes the public folder
app.use(express.static(__dirname + '/public'));
You can put them wherever you want. I usually create a /conf directory right off the project root and put config files in there, in JSON format.