Best way to retrieve and filter a directus collection inside a custom endpoint? - directus

I would need to search an item inside a collection and then work on that item.
What is the correct way to query a collection inside a custom endpoint?
Should i use the API or do you provide any classes?
I tried to follow the current guide https://github.com/directus/docs/blob/master/api/data.md
but i get a 403 (Forbidden) error.

I came around the same problem and now working with directus for some time. I asked the same question at the discussion section but the example in the guidebook should help:
export default (router, { services, exceptions }) => {
const { ItemsService } = services;
const { ServiceUnavailableException } = exceptions;
router.get('/', (req, res, next) => {
const recipeService = new ItemsService('recipes', { schema: req.schema, accountability: req.accountability });
recipeService
.readByQuery({ sort: ['name'], fields: ['*'] })
.then((results) => res.json(results))
.catch((error) => {
return next(new ServiceUnavailableException(error.message));
});
});
};
Maybe it is a bit late but hopefully it inspires others. Directus is from my first experiences now really great and worth to try.

Related

express-validator on PUT methods

I'm creating an API and decided to use express-validator for validation (duh), I've never used this before so I'm unsure on some aspects of it so my validations might not be the best but I'm getting by.
I have built two validation middle ware using this and export them from the same folder like this:
module.exports = {
create: require('./create'),
update: require('./update')
}
So I can then do this in my router:
const validation = require('../validations/plotValidation')
// ...
router.get('/', controller.all)
router.post('/create', validation.create(), controller.create)
router.get('/:plotId', controller.read)
router.put('/:plotId/update', validation.update(), controller.update)
router.delete('/:plotId/delete', controller.delete)
// ...
I'm not good enough with express-validator to do both validate both routes with the same file, maybe I'll try it at some point, anyway.
The .post method works fine and validates everything I want it to however the .put method just seems to be ignoring every check here are is file in case you want to see the checks:
const { body, check, param } = require('express-validator');
module.exports = () => {
return [
param('plotId')
.exists().withMessage('URI requires plot id'),
body('price')
.optional()
.isObject()
]
}
As you can probably tell I only just stated it, but even with only these two tiny checks it just doesn't seem to run.
Does express-validator not work on PUT methods?
For anyone else who has this issue I solved this by using .run on my checks, you can read more about this here essentially this is the code that saved me:
// parallel processing
const validate = validations => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
res.status(400).json({ errors: errors.array() });
};
};

How to Use Multiple Route in ExpressJS?

Is it possible to make router in ExpressJS like this?
users.js
const userController = ('../controllers/userController.js');
router.get('/:userName', userController.paramByUsername);
router.get('/:id', userController.paramByUserId);
In the controller, the code look like this
userController.js
function paramByUsername(req, res) {
User.findOne({
where: {
userId: req.params.userId
}
})
.then((user) => {
if(!user) {
return res.status(404).json({ message: "User not found."});
}
return res.status(200).json(user);
})
.catch((error) => {
return res.status(400).json(error);
});
}
function paramByUserId(req, res) {
User.findByPk(req.params.id)
.then((user) => {
if(!user) {
return res.status(404).json({ message: "User not found."});
}
}).catch((error) => {
return res.status(400).json(error);
});
}
By the code above, what I wanted to achieve is the endpoint like this:
/users/1 this should response same as /users/username.
I have tried the code above, but what I see is an error when I get /users/:id
You can't do both of these together:
router.get('/:userName', userController.paramByUsername);
router.get('/:id', userController.paramByUserId);
From a pure routing point of view, there is no way to tell the difference between these two. Whichever route you declare first will grab everything at the top level and the second will never get hit.
So, in route design, you HAVE to make sure that each route is uniquely recognizable to the Express route matcher based on what you put in the route pattern.
I suppose that if an id was always just numbers and a username could never be just numbers, then you could use a regex route and match only numbers for the id and everything else for the username, but that seems a bit fragile to me and I'd prefer something a bit more explicit.
I don't know the overall landscape of your app, but you may want to do:
router.get('/user/:userName', userController.paramByUsername);
router.get('/id/:id', userController.paramByUserId);
Or, you could use the query string with URLs like this:
/search?user=John
/search?id=4889
And, then you'd just have one route:
router.get("/search", ....);
And you would examine which properties are present in req.query to decide which item you were looking for.

Why is it considered poor practice to use Axios or HTTP calls in components?

In this article, it says:
While it’s generally poor practice, you can use Axios directly in your components to fetch data from a method, lifecycle hook, or whenever.
I am wondering why? I usually use lifecycle hooks a lot to fetch data (especially from created()). Where should we write the request calls?
Writing API methods directly in components increases code lines and make difficult to read.
As far as I believe the author is suggesting to separate API methods into a Service.
Let's take a case where you have to fetch top posts and operate on data. If you do that in component it is not re-usable, you have to duplicate it in other components where ever you want to use it.
export default {
data: () => ({
top: [],
errors: []
}),
// Fetches posts when the component is created.
created() {
axios.get(`http://jsonplaceholder.typicode.com/posts/top`)
.then(response => {
// flattening the response
this.top = response.data.map(item => {
title: item.title,
timestamp: item.timestamp,
author: item.author
})
})
.catch(e => {
this.errors.push(e)
})
}
}
So when you need to fetch top post in another component you have to duplicate the code.
Now let's put API methods in a Service.
api.js file
const fetchTopPosts = function() {
return axios.get(`http://jsonplaceholder.typicode.com/posts/top`)
.then(response => {
// flattening the response
this.top = response.data.map(item => {
title: item.title,
timestamp: item.timestamp,
author: item.author
})
}) // you can also make a chain.
}
export default {
fetchTopPosts: fetchTopPosts
}
So you use the above API methods in any components you wish.
After this:
import API from 'path_to_api.js_file'
export default {
data: () => ({
top: [],
errors: []
}),
// Fetches posts when the component is created.
created() {
API.fetchTopPosts().then(top => {
this.top = top
})
.catch(e => {
this.errors.push(e)
})
}
}
It's fine for small apps or widgets, but in a real SPA, it's better to abstract away your API into its own module, and if you use vuex, to use actions to call that api module.
Your component should not be concerned with how and from where its data is coming. The component is responsible for UI, not AJAX.
import api from './api.js'
created() {
api.getUsers().then( users => {
this.users = users
})
}
// vs.
created() {
axios.get('/users').then({ data }=> {
this.users = data
})
}
In the above example, your "axios-free" code is not really much shorter, but imagine what you could potentially keep out of the component:
handling HTTP errors, e.g. retrying
pre-formatting data from the server so it fits your component
header configuration (content-type, access token ...)
creating FormData for POSTing e.g. image files
the list can get long. all of that doesn't belong into the component because it has nothing to do with the view. The view only needs the resulting data or error message.
It also means that you can test your components and api independently.

Restify: Set default formatter

Also asked in official Restify repo: #1224
Hi,
Is it possible to have one default formatter that can handle any accept type that is not defined.
For Example:
restify.createServer({
formatters: {
'application/json': () => {},
// All other requests that come in are handled by this, instead of throwing error
'application/every-thing-else': () => {}
}
});
By all appearances, this is impossible. Since the formatters are stored in a dictionary, there is no way to create a key that matches every input (that would kind of defeat the point of a dictionary anyway...) The only way to accomplish this kind of thing outside of JSON would be with a regular expression, and regular expressions don't work with JSON.
Here is a program I wrote to test this.
var restify = require("restify");
var server = restify.createServer({
formatters: {
'application/json': () => { console.log("JSON") },
"[\w\W]*": () => { console.log("Everything else") } // Does not work
}
});
server.get("/", (req, res, next) => {
console.log("Root");
res.setHeader("Content-Type", "not/supported");
res.send(200, {"message": "this is a test"});
next()
});
server.listen(10000);
Also here is a link to the documentation on this in case you can find some hint that I couldn't see.
Restify documentation

How to best create a RESTful API in Node.js [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm a beginner in Node (and generally all back-end web development), and I have started to write a RESTful API in Node. There are a few things I'm trying to get my head around.
My application uses Express and Mongoose, and I am using the express-resource module to easily create my CRUD routes for the API resources. But there are a couple of things I am unhappy about, and think I could do better.
The first is Mongoose. If I want to write tests for my API, I have no way of stubbing Mongoose to force it to in memory data. All of the tutorials out there seem to point to Mongoose, however, and I'm really not sure what I should be using.
Secondly, my resource seems to have a lot of boilerplate code. Is this really the best way to create a RESTful API in Node.js? Are there other modules that will help me to create my CRUD routes? I believe there are ways you can create CRUD routes right from your schema, without anymore code, but I'm really not sure how.
I have seen projects out there such as Tower.js and CompoundJS (formally RailwayJS) that seem to be these comprehensive solutions that solve much more than my issues here. Perhaps I should be using them, but I really only want the Node.js application to be an API and nothing more. I am dealing with the front-end independently of the API.
To provide some context, here is my current situation. Currently, I have a model defined in Mongoose:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
, Link
var LinkSchema = new Schema({
uri: String,
meta: {
title: String,
desc: String
},
shares: [{
uid: Schema.Types.ObjectId,
date: Date,
message: String
}]
})
Link = module.exports = mongoose.model('Link')
Next, I define the controllers for the CRUD routes:
var mongoose = require('mongoose')
, _ = require('underscore')
, Link = mongoose.model('Link')
exports.load = function (req, id, fn) {
Link.findById(req.params.link, function (err, link) {
if (err) {
return res.send(err)
}
fn(null, link)
})
}
exports.index = function (req, res) {
var filterByUser = req.query.user ? { 'shares.uid': req.query.user } : {}
Link.find(filterByUser, function (err, links) {
if (err) {
return res.send(err)
}
res.send(links)
})
}
exports.create = function (req, res) {
var link = new Link(req.body)
link.save(function (err) {
if (err) {
// TODO: send 404
return res.send(err)
}
res.send(link)
})
}
exports.show = function (req, res) {
res.send(req.link)
}
exports.update = function (req, res) {
req.link = _(req.link).extend(req.body)
req.link.save(function (err, link) {
if (err) {
return res.send(err)
}
res.send(link)
})
}
exports.patch = exports.update
exports.destroy = function (req, res) {
req.link.remove(function (err) {
if (err) {
return res.send(err)
}
res.send()
})
}
Finally, I use the express-resource module to map these controllers to the necessary CRUD routes on top of the Express app.
app.resource('api/links', require('../resources/links'))
You should look into restify
If you want to use express, you can also check out this project that I made -- called node-restful.
This library seems to be much more mature and have more features though: https://github.com/jspears/mers
Strongloop Loopback seeems to be another good alternative for generating Node/MongoDB APIs. It can also generate mocha tests too.
Take a look at Hapi its a configuration-centric framework for building web applications and APIs its used as restful service.
Other options are sails.js and actionhero
Strapi is a new (2015) framework.
The admin interface on their website allows you to create an API and specify relationships between models. (As can be seen in their introduction video.)
However it is designed to run on the Koa server, not on Express.
I recommend Baucis + Express. Thousands of users, model-driven design based on Mongoose, very flexible, spec-compliant, HATEOAS/Level 3 ready. Fits all my needs perfectly. But then, I'm the author :) https://github.com/wprl/baucis
Check this out:
With Feathers you can build prototypes in minutes and production ready real-time backends and REST APIs in days. Seriously.
Here is the issues about the frameworks nowadays.
When you come here from google searching "best", "fastest" framework blah, blah, people will drop a line says "Hey, you should try this sails.js, feathers, Derby etc..."
Ok The question is:
- Are you just for fun with those frameworks - if yes, you can easily get a list of frameworks and then start to benchmark them whatsoever.
I am assuming most of people are "I want to build a product, build a site can make money in the future, or at least it will become popular";
Ok, all you search keywords and attentions here is wrong, try to search "production ready", "enterprise ready", "case study" those keywords then, or maybe go to indeed.com and search node.js, further dig out what node.js framework most companies using, the answer maybe just simply say "express".
if so, From node.js stack, The frameworks will pretty much be narrowed down a few of them: Hapi, Strongloop, or even not popular one Mojito from Yahoo
For Those frameworks at least you can tell - "They are really production or enterprise ready" - cos' they have been using form Walmart, from Yahoo, from other big giants from some time, some even for couple of years.
Can this explain why Ruby on rails and Django still dominate the full stack framework markets? even you see lots of "cool" frameworks keeping on coming to the market, Meteor, Sails.js, Go's Revel, Java's Play Spark whatever you can name - It doesn't mean these frameworks worse than the two, just mean they need time to win the market.
Another problems lots of the current frameworks are a kind of all-in-one, clone of "Ror"; From the end user's prospect, They just need a thing to help them get things done, need productive, need something to work from the begin to the end, like Ruby on Rails, like MacOS, like windows, like anything itself which has been tested by the time, has been daily used by people.
I've been using Express to build my RESTful APIs on Node.js and with the addition of Router in Express 4, it's even easier to structure it. It's detailed here http://www.codekitchen.ca/guide-to-structuring-and-building-a-restful-api-using-express-4/
Try https://hivepod.io/ and generate your example in a full MEAN stack. Hivepod builds on top of BaucisJS + ExpressJS + MongoDB + AngularJS.
Disclaimer: I work building Hivepod.
Have a look into this link
This project is build using the same project archutecture which is followed by ASP.Net WebApi 2.0. That means it will be having controllers, authentication mechanism etc. to start with. All you need to do is create your own controllers.
I'm surprised no-one mentioned Nodal.
From the website:
Nodal is a web server for Node.js, optimized for building API services quickly and efficiently.
Boasting its own opinionated, explicit, idiomatic and highly-extensible framework, Nodal takes care of all of the hard decisions for you and your team. This allows you to focus on creating an effective product in a short timespan while minimizing technical debt
It's actively maintained, has 3800+ stars on Github (at the time of writing), has a command-line tool built-in to generate boilerplate code, and overall gets the job done quickly.
This is a sample to perform CRUD operations in a library system
var schema=require('../dbSchema');
var bookmodel=schema.model('book');
exports.getBooks = function (req,res) {
bookmodel.find().exec().then((data)=>{
res.send(data)
}).catch((err)=>{
console.log(err);
});
};
exports.getBook = function (req,res) {
var bkName=req.params.Author;
bookmodel.find({Name:bkName}).exec().then((data)=>{
res.send(data)
}).catch((err)=>{
console.log(err);
});
};
exports.getAutBooks = function (req,res) {
bookmodel.find({},'Author').then((data)=>{
res.send(data);
}).catch((err)=>{
console.log(err);
});
};
exports.deleteBooks=function(req,res){
var bkName=req.params.name;
bookmodel.remove({Name:bkName}).exec().then((data)=>{
res.status(200);
console.log(bkName);
}).catch((err)=>{
console.log(err);
});
};
exports.addBooks=function(req,res){
var newBook=new bookmodel({
Name:req.body.Name,
ISBN:req.body.ISBN,
Author:req.body.Author,
Price:req.body.Price,
Year:req.body.Year,
Publisher:req.body.Publisher
});
newBook.save().then(()=>{
res.status(201);
}).catch((err)=>{
console.log(err);
});
};
I am a big fan of express and I've been using it to build RESTful APIs on Node.js which are easier to build. However, when our application started growing, we ended in a situation where express structure did not scale well and with more code splitting around, it was harder for us to maintain.
I am from C#/Java background where SOLID principles are heavily used. Frameworks like Java Spring / C# WebAPI are proven to create enterprise level applications.
I wanted to have a framework where I can reuse my existing C#/Java skills (reuse I mean MVC architecture, OOPS, SOLID, DI, Mocks ... yeah, strong typing). Unfortunately, I did not find a framework which meets my requirements (It should have less learning curve, minimalist codebase size, completely express compatible).
What do I mean by completely express compatible? Whatever express does, I must be able to do it even If I use a framework on top of it, when I have looked into Strongloop Loopback it was pretty good to use but It had a lot of documentation to go through and the frameworks are coupled, not really what I was looking for.
So I have created Dinoloop powered by Typescript (has interfaces, classes, abstract classes and strong oops). The package is pretty stable now.
With Dinoloop you can build enterprise level applications in a scalable architecture.
It uses Dependency Injection framework but you can configure any DI frameworks available in typescript. Dinoloop enables typescript to use as a Nodejs REST framework that helped me to maintain common typescript codebase for both angular and node projects.
So, Dinoloop is a perfect fit for typescript lovers and angular developers.
var mongoose = require('../DBSchema/SchemaMapper');
var UserSchema = mongoose.model('User');
var UserController = function(){
this.insert = (data) => {
return new Promise((resolve, reject) => {
var user = new UserSchema({
userName: data.userName,
password: data.password
});
user.save().then(() => {
resolve({status: 200, message: "Added new user"});
}).catch(err => {
reject({status: 500, message: "Error:- "+err});
})
})
}
this.update = (id, data) => {
return new Promise((resolve, reject) => {
UserSchema.update({_id: id}, data).then(() => {
resolve({status: 200, message: "update user"});
}).catch(err => {
reject({status: 500, message: "Error:- " + err});
})
})
}
this.searchAll = () => {
return new Promise((resolve, reject) => {
UserSchema.find().exec().then((data) => {
resolve({status: 200, data: data});
}).catch(err => {
reject({status: 500, message: "Error:- " + err});
})
})
}
this.search = (id) => {
return new Promise((resolve, reject) => {
UserSchema.find({_id:id}).exec().then(user => {
resolve({status: 200, data: user});
}).catch(err => {
reject({status: 500, message: "Error:- " + err});
})
})
}
this.delete = (id) => {
return new Promise((resolve, reject) => {
UserSchema.remove({_id:id}).then(() => {
resolve({status: 200, message: "remove user"});
}).catch(err => {
reject({status: 500, message:"Error:- " + err});
})
})
}
}
module.exports = new UserController();
///Route
var express = require('express');
var router = express.Router();
var Controller = require('./User.Controller');
router.post('/', (req, res) => {
Controller.insert(req.body).then(data => {
res.status(data.status).send({message: data.message});
}).catch(err => {
res.status(err.status).send({message: err.message});
})
});
router.put('/:id', (req, res) => {
Controller.update(req.params.id, req.body).then(data => {
res.status(data.status).send({message: data.message});
}).catch(err => {
res.status(err.status).send({message: err.message});
})
});
router.get('/', (req, res) => {
Controller.searchAll().then(data => {
res.status(data.status).send({data: data.data});
}).catch(err => {
res.status(err.status).send({message: err.message});
});
});
router.get('/:id', (req, res) => {
Controller.search(req.params.id).then(data => {
res.status(data.status).send({data: data.data});
}).catch(err => {
res.status(err.status).send({message: err.message});
});
});
router.delete('/:id', (req, res) => {
Controller.delete(req.params.id).then(data => {
res.status(data.status).send({message: data.message});
}).catch(err => {
res.status(err.status).send({message: err.message});
})
})
module.exports = router;
//db`enter code here`schema
var mongoose = require('mongoose');
const Schema = mongoose.Schema;
var Supplier =new Schema({
itemId:{
type:String,
required:true
},
brand:{
type:String,
required:true
},
pno:{
type:String,
required:true
},
email:{
type:String,
required:true
}
});
mongoose.model('Inventory',Inventory);
mongoose.model('Supplier',Supplier);
mongoose.connect('mongodb://127.0.0.1:27017/LAB', function (err) {
if (err) {
console.log(err);
process.exit(-1);
}
console.log("Connected to the db");
});
module.exports = mongoose;