Mongoose, ExpressJs - exposing mongo documents - node.js

Every example of using expressjs and mongoose are like that:
const contentTypes = await ContentType.find().sort({createdAt: -1});
res.json(contentTypes);
But in this scenario we are returning all document by REST api (even mongoose version field '_v'. I think it would be good practive to describe interface like this
export class ContentTypeEntry {
id: string;
name: string;
}
and convert mongoose type to this interface object and return this DTO. I just starting using nodejs ecosystem, so maybe in this ecosystem returning directly ORM objects is normal?
How are You dealing with moongose objects and REST endpoints?

I'm not entirely sure if I got the question right, but this is what my response object looks like-
// GET /api/products/1010
{
"meta": {
"type": "success",
"code": 200,
"message": "Retrieved product number 1010"
},
"data": {
"id": 1010,
"name": "Apple iPhone X",
"description: "Yada yada",
"price": 1000
}
}
This just separates the metadata and the actual returned data to make it easier for whoever is consuming the api to handle errors better. I also modify the JSON object to return only the required data and omit things like the version field from the response.

I think this is a great question, even if it's a bit broad. There are lots of frameworks that build on top of Node/Express (for example, LoopBack) to act as the glue between your data layer and your HTTP layer (REST, API, whatever you want to call it), deciding what you want to actually exist at a given endpoint. Happy to share other thoughts here if you have more specific questions.
You could also stay fairly lean and override the toJSON method of your Mongoose object (see this for an example). This is probably in line with your example of having an additional class that your object will conform to before being delivered to the end user, but personally I'd prefer to keep that within my object definition (the Mongoose model file).
I suppose at the end of the day, it's a question of how much control you need, how big the project is going to be and what its future needs will be. For microservices, you may find that Express + Mongoose and a couple of specific strategies will solve your concerns.

Related

NodeJS - Simplify/Resolve GraphQL query

I am currently writing a Lambda authorizer for an AWS AppSync API, however the authorization depends on the target resource being accessed.
Every resource has their own ACL listing the users and conditions for allowing access to it.
Currently the best I could find would be to get the identity of the caller, look at all the ACLs, and authorize the call while denying access to all the other resources, what's not only highly inefficient, but also extremely impractical, if not impossible.
The solution I had originally came up with was to get the target resource, retrieve the ACL and check if the user fits the specified criteria. The problem is that I am unable to reliably define what's the target resource. What I get from AWS is a request like this:
{
"authorizationToken": "ExampleAUTHtoken123123123",
"requestContext": {
"apiId": "aaaaaa123123123example123",
"accountId": "111122223333",
"requestId": "f4081827-1111-4444-5555-5cf4695f339f",
"queryString": "mutation CreateEvent {...}\n\nquery MyQuery {...}\n",
"operationName": "MyQuery",
"variables": {}
}
}
So, I only have the query string and variables, leaving the actual parsing of this to me. I got to convert it to an AST using graphql-js, but it's still extremely verbose and most importantly, it's structure varies greatly.
My first code to retrieve the target worked for the AppSync console queries, but not the Amplify Front-End, for example. I also can't rely on something as simple as the variable name, as an attacker could quite easily craft a query with an arbitrary name, or even not use variables at all.
I thought about implementing this authorization logic within Lambda Resolvers, what should be simpler in a way, but would require me to use resolvers as authorizers, what doesn't seem ideal, and implement the entire resolver logic when I just want the most trivial possible resolvers.
Ideally I'd like something like this:
/* Schema:
type Query {
operationName(key: KEY!): responseType
}*/
/* Query:
query abitraryQueryName($var1: KEY!) {
operationName(key: $var1) {
field1
field2
}
}*/
/* Variables:
{ "var1": "value1" } */
parsedQuery = {
operation: "operationName",
params: { "key": "value1" },
fields: [ "field1", "field2" ]
};
Is there any way to resolve/simplify the queries from GraphQL to JSON/similar in a way that this information can be easily extracted?
Well, couldn't find anything on it, so I made something myself.
On the off chance someone needs something similar, here's the gist with the code I used: https://gist.github.com/Iorpim/6544dad46060522dd0b17477871bc434
I didn't make it a proper full lib, as it's a very specific use case and it's likely a one-off, and I am also not sure how reliable it is, but it solves my problem!

Express with pug, Postgres and proper MVC

I recently started using Node.js + Express.js (generated with pug) + pg-promise for handling db.
My first target is to obtain data from Postgres (already set up) and display it pretty using render and pug. Let's say it is user list from Users table.
On this restful tutorial I have learned how to get data and return it as JSON - it worked.
Based on Mozilla's tutorial I seperated my code:
routes/users.js: where for '/' I call user_controller.user_list method (using router.get)
controllers/userController.js I have exported user_list where I would like to ask model for data and call render if I have results
queries.js which is kinda my model? But I'm not sure. It has API: connection to db with promises and one function for every query I am going to use in Controllers. I believe I should have like one Model file per table (or any logical entity) but where to store pgp connections?
This file is based on first tutorial I mentioned
// queries.js (connectionString is set properly to my postgres)
var pgp = require('pg-promise')(options);
var db = pgp(connectionString);
function getUsers(req, res, next) {
db.any('SELECT (user_id, username) FROM public.users ORDER BY user_id ASC LIMIT 1000')
.then(function (data) {
res.json({ data: data });
})
.catch(function (err) {
return next(err);
});
}
module.exports = {
getUsers: getUsers
};
Here starts my problem as most tutorials uses mongoose which is very model-db-schema-friendly and what I have is simple 'SELECT ...' string I pass to pg-promise's any() function.
Therefore I have no model class like User.
In userControllers.js I don't know how to call getUsers() to handle its data. Returning JS object from getUsers() would be nice.
Also: where should I call render? In controller or only in
db.any(...).then(function (data) { <--here--> })
Before, I also tried to embed whole Postgres handling into Controller but from db.any() I got this array for handling:
[{ row: '(1,John)' },{ row: '(2,Amy)' },{ row: '(50,Peter)' } ]
Didn't know how go from there as I probably lost my API functionality as well ;-)
I am browsing through multiple tutorials how to handle MVC but usually they handle MongoDB and
satisfy readers with res.send() not render().
I am not sure that I understand what your question is exactly about, but since I do not have enough reputation to comment, I'll do my best to help you with your interrogations. :)
First, regarding the queries.js file, it is IMO not exactly a model, but rather a DAO (Data Access Object) file. DAO comes between you Model (which is actually you database) and your Controller layers. There usually is a DAO file per object (User, Pet, whatever you want) in your data model.
When the data model is rather complex, it can be useful to use an Object Relational Mapping (ORM) such as Mongoose to map your database and execute complexe processes on your objects. In such a case, you might need a specific file per object so as to describe your model and store your queries. But since you don't need an ORM, you DAO can directly interact with your database. That is why you do not have a User.js file.
Regarding the way the db object should be used, I think you should refer directly to pg-promise documentation on the matter.
IMPORTANT: For any given connection, you should only create a single
Database object in a separate module, to be shared in your application
(see the code example below). If instead you keep creating the
Database object dynamically, your application will suffer from loss in
performance, and will be getting a warning in a development
environment (when NODE_ENV = development)
As a matter of fact, a db object in pg-promise sort of represents the database itself and is actually designed for the simultaneous use of several databases, which does not seem to be your case for the moment.
Finally, when it comes to the render function, I believe it should be in the controller, as your DAO is not supposed to know how the data it has gathered is going to be used.
Modularity is always a time-saving choice on the long-term.
Furthermore, note that you might later need a Business Layer between your DAO and your controller, in order to preprocess and postprocess data you are going to persist or to display. In such a case, if you need for instance to ask for data from your database, you will need to render data after it is processed by the Business layer. If the render is made in the DAO layer, it will not be possible.
In the link I provided earlier to pg-promise's db object connection, you will also find documentation on the any() method. You might already have looked it up.
It specifically states that it returns
A promise object that represents the query result:
When no rows are returned, it resolves with an empty array.
When 1 or more rows are returned, it resolves with the array of rows.
so your returned data is a JS Array. If you want to make it a JS object, just use
JSON.stringify(yourArray) to process your data before rendering it in your controller.
But I wonder if Pug is not able to use your data directly.
Also, if you cannot get any data out of your DAO, maybe you should check that your data object is not empty, as such a case is tolerated by the any() method. If you expect your query to always return something, you might want to consider using the many() or the one() methods.
I hope this helps you.

GraphQL functionality with plain JavaScript/JSON

I’m trying to understand what the advantages are of GraphQL. I’ve read about reducing the number of endpoints and the complexity of server responses, but it seems that the same results can be achieved with JS alone.
Here’s an example of a data object that could be sent as JSON to a node server with MongoDB. This would be an example of a game app where the client is retrieving user info:
let data = {
db: "users",
params: {_id: "xxxxx"},
fields: ["username", "level"],
games:
{
db: "games",
params: {userID: "xxxxx"},
fields: ["opponent”]
}
}
In this example, db, params, and fields would be standard keys, and games would be like a special key for the specific purpose of retrieving the user’s games, however, the syntax of the games object would follow the same standard format as the overall data object.
Then on the server, the Mongo query would look something like this:
db.collection(data.db).find(params)
You’d then filter out the extraneous Mongo fields in some standardized way and respond to the client.
I’m a relative beginner with JS, but I think you could also chain promises based on whether certain special keys (e.g., “games” from above) are included in the data object.
This seems like it achieves the same benefits as GraphQL with less complexity. What other benefits does GraphQL have that a plain JS equivalent does not?

CouchDB Read Configuration from design document

I would like to store a value in the config file and look it up in the design document for comparing against update values. I'm sure I have seen this but, for the life of me, I can't seem to remember how to do this.
UPDATE
I realize (after the first answer) that there was more than one way to interpret my question. Hopefully this example clears it up a little. Given a configuration:
curl -X PUT http://localhost:5984/_config/shared/token -d '"0123456789"'
I then want to be able to look it up in my design document
{
"_id": "_design/loadsecrets",
"validate_doc_update": {
"test": function (newDoc,oldDoc) {
if (newDoc.supersecret != magicobject.config.shared.token){
throw({unauthorized:"You don't know the super secret"});
}
}
}
}
It's the abilitly to do something like the magicobject.config.shared.token that I am looking for.
UPDATE 2
Another potentially useful (contrived) scenario
curl -X PUT http://trustedemployee:5984/_config/eventlogger/detaillevel -d '"0"'
curl -X PUT http://employee:5984/_config/eventlogger/detaillevel -d '"2"'
curl -X PUT http://vicepresident:5984/_config/eventlogger/detaillevel -d '"10"'
Then on devices tracking employee behaviour:
{
"_id": "_design/logger",
"updates": {
"logger": function (doc,req) {
if (!doc) {
doc = {_id:req.id};
}
if(req.level < magicobject.config.eventlogger.detaillevel ){
doc.details = req.details;
}
return [doc, req.details];
}
}
}
Here's a follow-up to my last answer with more general info:
There is no general way to use configuration, because CouchDB is designed with scalability, stability and predictability in mind. It has been designed using many principles of functional programming and pure functions, avoiding side effects as much as possible. This is a Good Thing™.
However, each type of function has additional parameters that you can use, depending on the context the function is called with:
show, list, update and filter functions are executed for each request, so they get the request object. Here you have the req.secObj and req.userCtx to (ab)use for common configuration. Also, AFAIK the this keyword is set to the current design document, so you can use the design doc to get common configuration (at least up to CouchDB 1.6 it worked).
view functions (map, reduce) don't have additional parameters, because the results of a view are written to disk and reused in subsequent calls. map functions must be pure (so don't use e.g. Math.random()). For shared configuration across view functions within a single design doc you can use CommonJS require(), but only within the views.lib key.
validate doc update functions are not necessarily executed within a user-triggered http request (they are called before each write, which might not be triggered only via http). So they have the userCtx and secObj added as separate parameters in their function signature.
So to sum up, you can use the following places for configuration:
userCtx for user-specific config. Use a special role (e.g. with a prefix) for storing small config bits. For example superLogin does this.
secObj for database-wide config. Use a special member name for small bits (as you should normally use roles instead of explicit user names, secObj.members.names or secObj.admins.names is a good place).
the design doc itself for design-doc-wide config. Best use the this.views.lib.config for this, as you can also read this key from within views. But keep in mind that all views are invalidated as soon as you change this key. So if the view results will stay the same no matter what the config values are, it might be better to use a this.config key.
Hope this helps! I can also add examples if you wish.
I think I know what you're talking about, and if I'm right then what you are asking for is no longer possible. (at least in v1.6 and v2.0, I'm not sure when this feature was removed)
There was a lesser-known trick that allowed a view/show/list/validation/etc function to access the parent design document as this in your function. For example:
{
"_id": "_design/hello-world",
"config": {
"PI": 3.14
},
"views": {
"test": {
"map": "function (doc) { emit(this.config.PI); })"
}
}
}
This was a really crazy idea, and I imagine it was removed because it created a circular dependency between the design document and the code of the view that made the process of invalidating/rebuilding a view index a very tricky affair.
I remember using this trick at some point in the distant past, but the feature is definitely gone now. (and likely to never return)
For your special use-case (validating a document with a secret token), there might be a workaround, but I'm not sure if the token might leak in some place. It all depends what your security requirements are.
You could abuse the 4th parameter to validate_doc_update, the securityObject (see the CouchDB docs) to store the secret token as the first admin name:
{
"test": "function (newDoc, oldDoc, userCtx, secObj) {
var token = secObj.admins.names[0];
if (newDoc.supersecret != token) {
throw({unauthorized:"You don't know the super secret"});
}
}"
}
So if you set the db's security object to {admins: {names: ["s3cr3t-t0k3n"], roles: ["_admin"]}}, you have to pass 's3cr3t-t0k3n' as the doc's supersecret property.
This is obviously a dirty hack, but as far as I remember, the security object may only be read or modified by admins, you wouldn't immediately leak your token to the public. But consider adding a separate layer between the CouchDB and your caller if you need "real" security.

Loading related entities when dealing with Models and Collections from Backbone to Express / Mongoose

I have a UserService object that is essentially a Service with additional configuration parameters and is attached to a User. In my View I would like to render a list of these UserServices however the model is formed as such:
UserService = Backbone.Model.extend({
defaults: {
id: 0,
user_id: 0, // This needs to reference the user object somehow
service_id: 0, // This needs to reference the service object somehow
length: 216000,
price: 1000
}
});
If I bind this model to the view, what is rendered ends up being the service_id instead of the parameter I need to render: service.name.
My questions are:
What should be stored in the UserService model at service? The full service object? Mongoose ID? Some other ID? (Please specify a suggestion)
Where should I get the information for this service.name / When should I pull the Service object to get that information? It would be nice to be able to do service.name in the view when rendering...
Is there a function to chain--upon loading the model, load related models that are needed?
Overall I just need an understanding of how related models work in Backbone / Express / Mongoose.
Any help is appreciated!
Update: After doing a bit of reading I have a couple different methods I can see:
Within the constructor / initializer load the Service object into the UserService object based on the reference ID returned from the server.
My questions with that one then become... what is the reference ID? Where do I put the newly retrieved object into, possibly in place of the ID?
Use the toJSON method to return an asthetic version of the UserService where it retreives the Service object and would return an object with the service name in it's place:
{
id: ???,
service_name: "this was retrieved from the service object in the toJSON method",
length: "1 hour", // converted from within the toJSON method
price: 10.00 // converted from cents to dollars in the toJSON method
}
Or maybe a combination? Thoughts?
Parse models handle loading related entities well, there is also library called Backbone Relational that can help with this.
Otherwise, my best recommendation is to store the object's ID and fetch the related entity upon success of fetching the first entity.
Anyone needing a code example just comment here and I'll see what I can come up with.

Resources