How to implement custom routes in sailsjs - javascript

I'm trying to understand sails.js. Firstly I try generate new api.
This is my code of models and controller
Coutries.js
module.exports = {
attributes: {
title:{
type:'string',
unique:true,
primaryKey:true
},
cities:{
collection:'Cities',
via:'country'
}
}
};
Cities,js
module.exports = {
attributes: {
title: {
type:'string',
unique:true,
primaryKey:true
},
country:{
model:'Countries',
type:'String'
},
description: {
type:'string'
}
}
};
Next in routes.js I wrote
'get /countries':'CountriesController.getAllCountries'
I need a list of countries.
I can not understand how to implement the function getAllCountries in CountriesController.js.
I use a local db in tmp directory.
Please, can somebody understand me in details how I can do this?
Also for understanding tell me how to implement a function addNewCountries and for example updateCitiesDescription.
Thanks and sorry for my english)

With sails, if you have a function to handle your routes, you create it inside api/controllers/CountriesController as a method.
For routes:
'get /countries':'CountriesController.getAllCountries'
Countries Controller:
module.exports = {
getAllCountries: function (req, res, next){
return Country.getAllCountries(function success(countries){
//handle your success logic e.g: return res.view("countries", {data: countries});
}, function errorHandle(error){
//error handling
})
}
}
Countries Model:
//inside your module.exports in api/models/Countries add this method
getAllCountries: function(cb, err){
Country.find({}).then(cb).catch(err);
}
To summarize, you use your controller method to contact the model and pass a success and error handling functions that will return an appropriate view.

Well, if your aim is to just view the list of countries then Sails has got you covered. It provides blueprint API's which you can directly use to query your db and view the response as JSON.
For example, you can call
http://localhost:1337/countries
to view all the countries in the db. Similarly you can carry out other queries as well directly using the API. Find more info about it here
However, if you would still like to query the database yourself to get the hang of it, what you've done so far is on the right track.
In your CountriesController, create a new action called "getAllCountries"
getAllCountries: function(req, res) {
Countries.find().exec(function(err, countries){
res.json(countries);
});
});
Your route basically tries to find a method named "getAllCountries" in the controller "CountriesController" and redirects your request to that action.
Upon receiving the call, you just fetch the list of countries from the database using waterline's query language and return the JSON back to the user.
A friendly advice, avoid naming your models in plural. For example, if you are trying to maintain countries and cities in your db, then name the models "Country" and "City".

Related

How to create a custom route in Sails JS

I'm trying to create a custom route in Sails and according to their documentation, if I add the following in config/routes.js:
'post /api/signin': 'AuthController.index',
The request would be dealt with by the index action in the AuthController but that doesn't seems to work at all. When I try the /api/login in Postman, I get nothing back.
Please note that I've added restPrefix: '/api' in my config/blueprints.js. Please note I'm using Sails 0.12.x
What am I missing here?
Since you are pointing to a controller with method index on it, you need to add it to your controllers and send a JSON response from there, (since you are using post). here is a simple example
config/routes.js
'post /api/signin': 'AuthController.index',
api/controllers/AuthController.js
module.exports = {
index: function(req, res) {
var id = req.param('id');
if(!id) {
return res.json(400, { error: 'invalid company name or token'});
}
/* validate login..*/
res.json(200, {data: "success"};
}
}
Update
Since you already have the above its probably caused by the blueprints you have.
Blueprint shortcut routes
Shortcut routes are activated by default in new Sails apps, and can be
turned off by setting sails.config.blueprints.shortcuts to false
typically in /config/blueprints.js.
Sails creates shortcut routes for any controller/model pair with the
same identity. Note that the same action is executed for similar
RESTful/shortcut routes. For example, the POST /user and GET
/user/create routes that Sails creates when it loads
api/controllers/UserController.js and api/models/User.js will
respond by running the same code (even if you override the blueprint
action)
with that being said from sails blueprint documentation, maybe turning off shortcuts and remove the prefix you've added.
possibly the shortcuts are looking elsewhere other than your controllers thus returning 404.
the prefix is being added to your blueprint connected route, hence you need /api/api/signin to access it?
Note
I am unable to replicate your issue on my computer as its working fine over here. but i have all blueprint settings turned off.
module.exports.blueprints = {
actions: false,
rest: false,
shortcuts: false,
// prefix: '',
pluralize: false,
populate: false,
autoWatch: false,
};

How do I get keystoneJS to run a function once a specific model has been updated?

I want to run a function once I have a new or updated item in a specific KeystoneJS model. How do I do that? Would I add an event... is there an event already? Do I add it in the model or elsewhere?
You can use mongoose middleware as with any non-keystone project. A keystone lists schema can be accessed with .schema, e.g.
var keystone = require('keystone');
var Types = keystone.Field.Types;
var User = new keystone.List('User');
User.add({
name: { type: Types.Name, required: true, index: true },
email: { type: Types.Email, initial: true, required: true, index: true },
});
//do stuff BEFORE the user document is fully saved to the DB
User.schema.pre('save', function(next){
console.log('SAVING USER:', this);
next();
});
//do stuff AFTER the user document has been saved to the DB
User.schema.post('save', function(user){
console.log('USER WAS SAVED:', user);
});
User.defaultColumns = 'name, email';
User.register();
Take a look at mongoose middleware, since some restrictions apply, for instance when doing mass updates the middleware will not run, this is by design in mongoose and has nothing to do with keystone.
KEystoneJS uses mongoose.
When you add new model, the CRUD(change,Replace,Update, Delete) happens naturally in Admin side(http:///keystone
However for non-admin side You need to make Routes, and in routes views you can use mongoose API to do so.
any change in in keystoneJS, can be made working just by restarting the server (nam start)

Sails.js Waterline query by association

I'm developing and app with Sails.js and using Waterline orm for db. I'm developing functionality for users to do friend requests and other similar requests to each other. I have following URequest model for that:
module.exports = {
attributes: {
owner: {
model: 'Person'
},
people: {
collection: 'Person'
},
answers: {
collection: 'URequestAnswer'
},
action: {
type: 'json' //TODO: Consider alternative more schema consistent approach.
}
}
};
Basically owner is association to Person who made the request and people is one-to-many association to all Persons who the request is directed. So far fine.
Now I want to have a controller which returns all requests where certain user is involved in meaning all requests where user is either in owner field or in people. How I do query like "give me all rows where there is association to person P" ? In other words how I ca know which URequest models have association to a certain Person?
I tried something like this:
getRequests: function (req, res) {
var personId = req.param('personId');
URequest.find().where({
or: [
{people: [personId]}, //TODO: This is not correct
{owner: personId}
]
}).populateAll().then(function(results) {
res.json(results);
});
},
So I know how to do the "or" part but how do I check if the personId is in people? I know I should somehow be able to look into join-table but I have no idea how and couldn't find much from Waterline docs relating to my situation. Also, I'm trying to keep this db-agnostic, though atm I'm using MongoDB but might use Postgres later.
I have to be honest this is a tricky one, and, as far as I know what you are trying to do is not possible using Waterline so your options are to write a native query using query( ) if you are using a sql based adapter or native otherwise, or try doing some manual filtering. Manual filtering would depend on how large of a dataset you are dealing with.
My mind immediately goes to reworking your data model a bit, maybe instead of a collection you have a table that stores associations. Something like this:
module.exports = {
attributes: {
owner: {
model: 'URequest'
},
person: {
model: 'Person'
}
}
Using the sailsjs model methods (like beforeCreate) you could auto create these associations as needed.
Good Luck, I hope you get it working!

'or' query with different data types in sails.js

Given routes
GET /user/42
GET /user/dude
where 42 is user id and dude is username.
So, I want to have method in my controller that return user for both cases. Here it is:
// api/controllers/UserController.js
findOne: function(req, res) {
User.findOne({
or: [
{id: req.params.id},
{username: req.params.id}
]
}).exec(function(err, user) {
if (err) {
return console.log(err);
}
res.json(user);
});
},
When I try to GET /user/42 everything is fine.
When I try to GET /user/dude I get error:
Error (E_UNKNOWN) :: Encountered an unexpected error
error: invalid input syntax for integer: "dude"
It seems like sails refuses to process {id: 'dude'} because of type mismatch.
I am using sails 0.10.5 with sails-postgresql 0.10.9. So what am I doing wrong?
UPD: I do know how to solve problem. Of course I can put if statement to my controller and check what type of parameter it got. Actually, I just created two routes with regexp parameters that point to single method.
My actual problem is why I can not do this with or. Does sails provide such way?
By default, sails get method only supports id numbers. You will need
to define custom routes and implement the relevant api method to
support a get request using a string instead of a number.
Technical Details:
To solve your exact problem, you need to define the following route in your routes.js
module.exports.routes = {
...
'/user/get/:param': 'UserController.findByIDorName',
...
}
This will pass anything after the /user/get/ as the parameter named 'param' to the findByIDorName controller method.
Next you need to check the type of the param and decide if you want to fetch the user info based on the id or the name, so add this function in your userController.js
findByIDorName : function (req, res) {
if (isNaN(req.param)){
//find the user by his username after any validation you need
}
else {
//find the user by his id and return
}
}

Querying associated models using MEAN stack

I’m trying to setup an model association using MEAN where an Epic has many Tasks. I’m creating the Epic first then associating it when creating a task. The task data model looks like this with the Epic associated:
task:
{ name: 'my first task',
epic:
{ name: 'My fist epic',
_id: 52f511c605456ba4c936180d,
__v: 0},
_id: 52f511d605456ba4c936180e,
__v: 0 } }
In my public Epics controller I’m trying to query for all the tasks with the current Epic’s ID but I’m I’m not having much luck. The query below returns ALL tasks instead of the tasks associated with my Epic.
Tasks.query({“epic._id": $routeParams.epicId}, function(tasks) {
$scope.tasks = tasks;
});
Is there a better way to do association and retrieval using MEAN? I’m a bit of a noob.
Thanks in advance!
EDIT:
I've playing around with the idea of updating the epic when a new task is created. In app/controllers/tasks.js I have this code that doesn't work.
exports.create = function (req, res) {
var task = new Task(req.body)
Epic.find(req.body.epic.id, function (err, epic) {
if (err) return next(err)
epic.tasks.push(task.id);
epic.save();
})
task.save()
res.jsonp(task)
}
Are you also using mongoose? I would use the "ref" and "populate".
First you have a TaskSchema.
var TaskSchema = new Schema({ ... });
mongoose.model('Task', TaskSchema);
add the model etc, then you you reference it in your other schema. I'll add an example of 1 or multiple task(s).
var Schema = new Schema({
task: {
type: Schema.ObjectId,
ref: 'Task'
},
tasks: [
{ type: Schema.Types.ObjectId, ref: 'Task'}
]
});
and then to call it with populate.
this.findOne({
_id: id
}).populate('tasks').exec(cb);
It looks like you need help debugging xhr. Let's trace the steps:
Is your request making it to the server?
Is it arriving at the correct express route?
Is your server-side code performing the correct find operation against Mongo?
Is Mongo returning the right results?
Are you writing the results from Mongo into the response correctly?
Are you able to see the response on the client by inspecting the network traffic using your browser's dev tools?
Are you handling the promise success correctly?
You'll need to post more info and code to know where the problem lies.

Categories