I want to ask about graphql.
In graphql, can we group query/resolver and then give middleware like laravel? If it's Laravel, we can group routes and then the group can be given middleware like this.
Route::middleware(['auth'])->group(function () {
Route::get('/', function () { ... });
Route::get('/blog', function () { ... });
});
Is providing one-by-one authentication in query/resolver the best way? Or is there a better way than providing authentication one by one? I want it so that there is no code duplication so the code is clean. I was thinking about making a function and then adding it to each query/resolver, but I'm not sure if this is the best way or not.
const resolvers = {
Query: {
blog: (_, { id, slug }) => {
if (!user) {
throw new Error("Unauthenticated!")
}
// return ...
},
blogs: (_, args, { user }) => {
if (!user) {
throw new Error("Unauthenticated!")
}
// return ...
}
}
}
Sorry if there are wrong words, because I am also still new to Graphql and not very good at English.
Related
I have a problem with doing Pagination. when including multiple & parameters. simply say, it doesn't work.
server.get("/search", async(req, res) => {
try {
const key = req.query.key;
const value = req.query.value;
const text = req.query.text;
let result = await collection.aggregate([
{
'$search': {
'text': {
'query': `${text}`,
'path': 'title'
}
}
},
//match key here...
]).toArray();
res.send(result)
} catch (error) {
console.error(error)
}
})
The problem you have there is how you structured your endpoint url.
app.get("/search/:text/:key?&:value?&.....", (req,res)) => {....}
If you want to get the values you send via query string, you don't have to add the query params to the endpoint's url, you can simply have it like so:
app.get("/search", (req,res)) => {....}
And then build the request to the API like this:
http://localhost:4000/search?text=mango&brand=rasna
Like this you can access the properties of the request in the route's controller:
app.get("/search", (req,res)) => {
const { text, brand } = req.query;
}
Optional values are fine, if you don't send them they will be undefined when you try to access their values in the controller so you can just check for them with conditionals.
app.get("/search", (req, res)) => {
const { text, brand } = req.query;
if(text) { ... }
if(brand) { ... }
}
Obviously this is a very simple implementation just to explain the problem.
i'm new to angular, the journey is going quite well for now but it's also a bit challenging. Managed to fix some issues with the application like insert dating successfully in the server and so on, but trying to retrieve it inside a listview in angular seems like its not working. Please assist
I am struggling to create a listview using data from nodejs in mysql.
angular is not giving me the data.
index.html:
<div class="message-list">
<ul class="message-thread">
<li ng-repeat="ppPack in data.pp"
>
{{data.pp}} Posted by {{data.username}}
</li>
</ul>
</div>
controller
$scope.selectFeed = () => {
appService.getFeedList().then( (response) => {
$scope.$apply(() => {
let ppPack = {
fromUserId: document.querySelector(" from_user_id").value,
post: document.querySelector("#post").value
}
});
$scope.data.pp = response.pp;
}).catch( (error) => {
console.log(error);
alert('Unexpected Error, Try go back and redo somethings you did!.');
});
}
helper class
async getFeedList(){
try {
return await this.db.query(
`SELECT * FROM posts ORDER BY id ASC`,
[params.from_user_id,params.posts]
);
} catch (error) {
console.warn(error);
return null;
}
}
service class
getFeedList() {
return new Promise((resolve, reject) => {
this.httpCall({
url: '/getPosts',
'posts': $scope.data.pp,
'from_user_id': $scope.data.username
}).then((response) => {
resolve(response);
}).catch((error) => {
reject(error);
});
});
}
ng-repeat uses the value that you pass to the dom.
Starting with your helper class, you need to pass out the value in which you want to collect from the db, even though it seems like you query everything, giving params value to them will make it easier for you to understand the logic of the code, which means you query should be:
async getFeedList(posts, from_user_id)){
try {
return await this.db.query(
`SELECT * FROM posts ORDER BY id ASC`,
[params.from_user_id,params.posts]
);
} catch (error) {
console.warn(error);
return null;
}
}
And in your service class, also pass the params
getFeedList(posts, from_user_id) {
return new Promise((resolve, reject) => {
this.httpCall({
url: '/getPosts',
'posts': posts,
'from_user_id': from_user_id
}).then((response) => {
resolve(response);
}).catch((error) => {
reject(error);
});
});
}
and in your controller file should be outside other scopes since they won't be called out unless they requesting the service on their own like this:
appService.getFeedList()
.then((response) => {
$scope.$apply(() => {
$scope.postsData = response.postsData;
//output to see the array before commit to the scope
console.log(response);
});
})
.catch((error) => {
console.log(error);
alert("error code!.");
});
and then finally in your home.html you can call
<div class="list-group">
<ul class="list-group-item"
ng-repeat="post in postsData">
{{post.post}} Posted by <strong>{{post.from_user_id}} </strong</ul>
</div>
Some best practise tips from the official angular site:
Best Practice: If you are working with objects that have a unique identifier property, you should track by this identifier instead of the object instance, e.g. item in items track by item.id. Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones. For large collections, this significantly improves rendering performance.
I do not quite understand how to properly break the logic on the controllers and models in nodeJS when working with the backend application. Suppose I have an example
This code is in the model of my application, and logically I understand that the model is only responsible for choosing from the database, and the controller and everything else should be done by the controller, but I don’t quite understand how to do this and I tried to transfer part of the code to the controller and export it, but I did not succeed (Please, help, at least with this example! The main thing for me is to understand the principle of working with MVC in the node !!!
exports.currentPostPage = function(req, res){
db.query('SELECT * FROM `posts`', function (err, result) {
if (err){
console.log(err);
}
var post = result.filter(item => {return (item.id == req.params.id)? item: false})[0];
if (post === undefined){
res.render('pages/404');
} else {
res.render('pages/post-page', {postId: req.params.id, item: post});
}
});
};
So, you're on the right track. There's a lot of different ways to do it depending on preferences, but one pattern I've seen pretty commonly is to use the callback as a way to integrate. For example, let's say you have your model file:
exports.getPostById = (id, cb) => {
db.query('SELECT * FROM `posts` WHERE id=?', [id], function (err, result) {
if (err){
return cb(err); // or, alternatively, wrap this error in a custom error
}
// here, your logic is just returning whatever was returned
return cb(null, result);
});
};
Note I also am letting the DB handling the ID lookup, as it's probably more efficient at doing so for larger data sets. You didn't say what DB module you're using, but all the good ones have some way of doing parametrized queries, so use whatever works w/ your DB driver.
Anyway, the Model file therefore handles just the data interaction, the controller then handles the web interaction:
// postController.js
const model = require('../models/postModel.js'); // or whatever you named it
exports.populatePost = (req, res, next, id) => {
model.getPostById(id, (err, post) => {
if (err) return next(err); // centralized error handler
req.post = post;
next();
});
}
export.getOnePost = (req, res, next) => {
if (req.post) {
return res.render('pages/post-page', req.post);
}
// again, central error handling
return next({ status: 404, message: 'Post not found' });
}
I have mentioned central error handling; I vastly prefer it to scattering error handling logic all over the place. So I either make custom errors to represent stuff, or just do like above where I attach the status and message to an anonymous object. Either will work for our purposes. Then, in a middleware file you can have one or more handler, the simplest like this:
// middleware/errors.js
module.exports = (err, req, res, next) => {
console.error(err); // log it
if (err.status) {
return res.status(err.status).render(`errors/${err.status}`, err.message);
}
return res.status(500).render('errors/500', err.message);
}
Finally, in your routing setup you can do things like this:
const postController = require('../controllers/postController');
const errorHandler = require('../middleware/errors.js');
const postRouter = express.Router();
postRouter.param('postId', postController.populatePost);
postRouter.get('/:postId', postController.getOnePost);
// other methods and routes
app.use('/posts', postRouter)
// later
app.use(errorHandler);
As was pointed out in the comments, some folks prefer using the Promise syntax to callbacks. I don't personally find them that much cleaner, unless you also use the async/await syntax. As an example, if your db library supports promises, you can change the model code to look like so:
exports.getPostById = async (id, cb) => {
// again, this assumes db.query returns a Promise
return await db.query('SELECT * FROM `posts` WHERE id=?', [id]);
}
Then your controller code would likewise need to change to handle that as well:
// postController.js
const model = require('../models/postModel.js'); // or whatever you named it
exports.populatePost = async (req, res, next, id) => {
try {
const post = await model.getPostById(id)
req.post = post
return next()
} catch (err) {
return next(err)
}
}
The question is: How can I import json from a URL specifically, NOT an internal file in Express, and contain it such that I can use it across multiple views. For example, I have a controller. How can I get in in there (controller)? I am using request.
I have a router with 2 routes but I want to have a bunch more, and the bulk of the logic for the routes is being done in controllers.
Below is a controller with the route for showing all. I had hardcoded a small piece of "json" in it as data to use temporarily, but now I want to populate my view via an outside api. This is my controller:
module.exports = {
//show all USERS
showDogs: (req,res) => {
const dogs = [
{
name:"Fluffy", breed:"ChowChow", slug:"fluffy", description:"4 year old Chow. Really, really fluffy."
},
{
name:"Buddy", breed:"White Lab", slug:"buddy", description:"A friendly 6 year old white lab mix. Loves playing ball"
},
{
name: "Derbis", breed:"Schmerbis",slug:"derbis", description:"A real Schmerbis Derbis"
}
];
res.render("pages/dogs", {dogs: dogs, title:"All Dogs"});
}
};
How can I get this json the data to come from an outside line? I have used request before but I don't know how to transfer the data between files. I don't want to put it inside the showDogs or it won't be accessible to other functions here. Right?
I had something like this below, with require('request') at the top of the controller, but it just gave errors.
const options = {
url:'https://raw.githubusercontent.com/matteocrippa/dogbreedjsondatabase/master/dog-breed.json',
method:'GET',
headers:{
'Accept-Charset': "utf-8"
NO IDEA ABOUT THIS AREA FOR NOW EITHER
}
I also tried wrapping the entire thing, all the functions, in a request:
request('https://raw.githubusercontent.com/matteocrippa/dogbreedjsondatabase/master/dog-breed.json', function(error, response, body)
But still I got an error.
And this the route.js where the controller sends:
//dogs
router.get('/dogs', dogsController.showDogs)
I am a Node beginner so the only thought I have is to write some middleware. The deeper problem here is I don't know how to use/write middleware properly. Perhaps I can become informed.
Add a utility file that contains the code to talk to the external API. Include this file and use it's function to get dogs data. Later, you can add more functions for other APIs as well.
const getDogData = require('../externalApis').getDogData;
module.exports = {
//show all USERS
showDogs: (req, res) => {
getDogData(function(err, dogs) {
if (err) {
//handle err
} else {
res.render("pages/dogs", {
dogs: dogs,
title: "All Dogs"
});
}
}
}
};
// externalApis.js
const request = require ('request');
module.exports = {
getDogData: function(done) {
const options = {
url: 'https://raw.githubusercontent.com/matteocrippa/dogbreedjsondatabase/master/dog-breed.json',
method: 'GET',
headers: {
'Accept-Charset': "utf-8"
}
}
request(options, function(error, response, body) {
if (error) {
return done(err);
} else {
var data = JSON.parse(body); // not sure how's data is returned or if it needs JSON.parse
return done(null, data.dogs); //return dogs
}
});
}
My API has three endpoints: articles, websites, and users. Each article is associated with a website. A user can also share articles.
In my API, I have just created an endpoint at /website/:id/articles. This will query the database for articles associated with the given website. It then performs some manipulation on the data for each article based on who is talking to the API ("has the user shared this article?", for example).
I am now moving on to create a similar endpoint at /users/:id/shared-articles. The database query for this is slightly different, but the manipulation I want to perform on the articles data following the query is the same as before.
Here is some pseudo code for the former endpoint:
router.get('/websites/:id/articles', function (req, res) {
articleService.find({ websiteId: req.params.id }, function (error, foundArticles) {
async.waterfall([
function (cb) {
// Manipulate foundArticles…
cb(null, manipulatedArticles)
},
function (articles, cb) {
// Manipulate articles some more…
cb(null, manipulatedArticles)
},
], function (error, articles) {
if (error) {
return res.json(error, 400)
}
res.json(articles)
})
})
})
To create my new endpoint, /users/:id/shared-articles, I could abstract the manipulation tasks into a function that can be shared by both of my endpoints (the waterfall seen above), reducing code repetition.
router.get('/websites/:id/articles', function (req, res) {
articleService.find({ websiteId: req.params.id }, function (error, foundArticles) {
manipulateArticles(foundArticles, function (articles) {
if (error) {
return res.json(error, 400)
}
res.json(articles)
})
})
})
router.get('/users/:id/shared-articles', function (req, res) {
shareActionService.find({ userId: req.params.id }, function (error, foundShareActions) {
var sharedArticleIds = { _id: { $in: _.pluck(foundShareActions, 'sharedArticleId') } }
articleService.find(sharedArticleIds, function (error, foundArticles) {
manipulateArticles(foundArticles, function (articles) {
if (error) {
return res.json(error, 400)
}
res.json(articles)
})
})
})
})
However, I figured that this sort of code re-use problem must be common when designing APIs in Node, and I would like to know if there is an obviously better solution that I am missing here.
One idea I had would be to have all article sub-resources (such as /users/:id/shared-articles or /websites/:id/links) talk to the /links API internally, which itself would deal with the manipulation I mention above. The problem then is that I would have to make /links very verbose in the query headers/parameters it needs, in order to allow for the different database queries needed (such as those by the two sub-resource endpoints demonstrated here).
Is there a better solution/abstraction here?
You can create a "service" layer. Abstract the link manipulation into a completely separate file and call it from each of the routes.
Create a service/links.js:
module.exports = {
manipulateLinks: function (response) {
// Manipulate code
return response
}
}
Then in you routes, call the function:
var linkservice = require('../service/links')
var response = linkservice.manipulateLinks(response)