Can't see relations - Loopback 4 + MongoDB + openapi-to-graphql - mongodb

Beforehand, hello to everyone!
I initialize Graphql in Loopback 4 like this:
const graphqlPath = '/graphql';
// #ts-ignore
const oas: Oas3 = await (<Oas3>app.restServer.getApiSpec());
console.log(graphqlHTTP);
const {schema} = await createGraphQLSchema(oas, {
strict: false,
viewer: true,
baseUrl: url,
});
//#ts-ignore
const handler: graphqlHTTP.Middleware = graphqlHTTP({
schema,
graphiql: true,
});
app.mountExpressRouter(graphqlPath, handler);
console.log(`Graphql: ${url}${graphqlPath}`);
Then I have a relation Favors>User (a User can have many Favors, but a Favor has only one User). I've created this relation with lb4 relation, and I haven't made any other change.
User Model:
#hasMany(() => Favor)
favors: Favor[];
Favor Model:
#belongsTo(() => User)
userId: string;
At the moment of the query (http://localhost:3000/graphql) this is what happens:
{
favors {
id
userId
user {
id
}
}
}
I have no idea if it has something to do with the loopback-connector-mongodb which does not match ObjectId correctly, but I have no clue how to solve this.
My package.json:
"#loopback/core": "^2.12.0",
"openapi-to-graphql": "^2.2.5",
"loopback-connector-mongodb": "^5.4.0",

Hello from the LoopBack team 👋
In order to allow openapi-to-graphql understand relations, the OpenAPI schema produced by a LoopBack application must describe Links between entities.
LoopBack 4 does not provide such metadata out of the box. We have been discussing this use case in GitHub issue loopback-next#2153, unfortunately we haven't found a clear solution yet.
What you can try: In your controllers, enhance your response specifications with a links section pointing to relevant API endpoints for accessing the related models.

Related

Mongodb Ref dynamic populate with grapqhl?

I have to decide whether to populate or not according to the query request, but I don't know how to do it.
So Example
If my model User is looks like this
below syntax is from typegoose and typegraphql
class User {
#Field()
#prop()
name: string;
#Field()
#prop(ref:"House")
house: Ref<House>
}
And here is two diffent query
Query1
user {
name
}
Query2
user {
name
house {
location
}
}
And in the resolver
User: () => {
const user = UserModel.find(blahblah)**.populate("house")**
return user
}
Query1 dose not need populate
but Query2 need
in same resolver!
I want to decide whether to populate or not depending on the requirements of the query.
I can't decide whether to populate or not without knowing what is the actual query was in resolver.
I found very similar question in stackoverflow
But there is not proper answer...
Solving relationships in Mongoose and GraphQL
i dont know much about graphql, but i think there is some method to get if that path is requested, so the following should work:
let query = Model.find(basicQuery);
if (req.path) { // replace this with graphql's method to get if the path is requested
query = query.populate(yourPath);
}
const doc = await query.exec();
PS: but as an comment already noted, i think there is some better method to do this in graphql (another resolver?)

How to handle unique indexes with mongoose?

The FAQ section of Mongoose 5 explitily says:
The unique option for schemas is convenient for development and documentation, but mongoose is not an index management solution.
This statement threw me off as I'm relatively new to Mongoose library. I'm making a basic model for User and adding a couple of tests for validation. Specifically validate no duplicate users are there.
The schema is simple
const schema = new Schema({
email: { type: String, required: true, unique: true },
...
Needless to say, my test fail:
const userA = new User({ email: 'a#a.com' });
const dupUserA = new User({ email: 'a#a.com' });
const promiseChain = userA.save().then(() => dupUserA.save());
expect(promiseChain).to.be.rejected // using chai-as-promised here
.then(err => {
// assertions about error message
});
The test fails cause the promise fulfills, meaning the save was successful.
I don't quite understand what Mongoose team means. I realize these schema are no database "migrations", but since the only example there is in the docs is some callback to the 'index' event, I'm lost.
How should I handle unique index? Is there a solution not to do them directly to the MongoDB through shell? Something that can reside in the codebase, equivalent to migrations?

Sailsjs - Prevent non-model fileds to be saved in mongo document

I recently started working with Sails and mongo.
I use Sails blueprints to generate part of my api.
The problem is, that the request body I send is being saved to the mongo collection, regardless of the fields defined in the model.
So for example, let's say I have the following Event model:
module.exports = {
attributes: {
title: {
type: 'string',
required: true
},
}
}
When I Send a POST request to the /event/ endpoint with the following params:
{"title":"Some Event", "random":"string"}
The saved mongo document contains also the "random":"string" value, even though it's not part of the model.
I've tried to come up with some common method to remove non-model attributes before creation for all models, but the possible solutions seemed not right and dirty.
Am I missing something?
Any help would be appreciated!
You can use schema option in your model. Just add it to model declaration and that's it.
// api/models/Model.js
module.exports = {
schema: true,
attributes: {
title: {
type: 'string',
required: true
}
}
};

Sailsjs and Associations

Getting into sails.js - enjoying the cleanliness of models, routes, and the recent addition of associations. My dilemma:
I have Users, and Groups. There is a many-many relationship between the two.
var User = {
attributes: {
username: 'string',
groups: {
collection: 'group',
via: 'users'
}
}
};
module.exports = User;
...
var Group = {
attributes: {
name: 'string',
users: {
collection: 'user',
via: 'groups',
dominant: true
}
}
};
module.exports = Group;
I'm having difficulty understanding how I would save a user and it's associated groups.
Can I access the 'join table' directly?
From an ajax call, how should I be sending in the list of group ids to my controller?
If via REST URL, is this already accounted for in blueprint functions via update?
If so - what does the URL look like? /user/update/1?groups=1,2,3 ?
Is all of this just not supported yet? Any insight is helpful, thanks.
Documentation for these blueprints is forthcoming, but to link two records that have a many-to-many association, you can use the following REST url:
POST /user/[userId]/groups
where the body of the post is:
{id: [groupId]}
assuming that id is the primary key of the Group model. Starting with v0.10-rc5, you can also simultaneously create and a add a new group to a user by sending data about the new group in the POST body, without an id:
{name: 'myGroup'}
You can currently only add one linked entity at a time.
To add an entity programmatically, use the add method:
User.findOne(123).exec(function(err, user) {
if (err) {return res.serverError(err);}
// Add group with ID 1 to user with ID 123
user.groups.add(1);
// Add brand new group to user with ID 123
user.groups.add({name: 'myGroup'});
// Save the user, committing the additions
user.save(function(err, user) {
if (err) {return res.serverError(err);}
return res.json(user);
});
});
Just to answer your question about accessing the join tables directly,
Yes you can do that if you are using Model.query function. You need to check the namees of the join tables from DB itself. Not sure if it is recommended or not but I have found myself in such situations sometimes when it was unavoidable.
There have been times when the logic I was trying to implement involved a lot many queries and it was required to be executed as an atomic transaction.
In those case, I encapsulated all the DB logic in a stored function and executed that using Model.query
var myQuery = "select some_db_function(" + <param> + ")";
Model.query(myQuery, function(err, result){
if(err) return res.json(err);
else{
result = result.rows[0].some_db_function;
return res.json(result);
}
});
postgres has been a great help here due to json datatype which allowed me to pass params as JSON and also return values as JSON

Custom REST url for specific Model

Is there a way in Ember to configure a custom REST url for a specific Model?
Like with this model:
App.Post = DS.Model.extend({
title: DS.attr('string'),
comments: DS.hasMany('App.Comment')
});
App.Comment = DS.Model.extend({
content: DS.attr('string'),
post: DS.belongsTo('App.Post')
});
And this Store:
app.Store = DS.Store.extend({
revision : 11,
adapter : DS.RESTAdapter.extend({
namespace : 'rest'
})
});
I want that the comments are retrieved via /rest/post/{id}/comments instead of /rest/comments which is the default behaviour.
Is it possible to configure a rest-url for one specific Model?
You can register an additional adapter and 'scope' it to your model.
App.Store.registerAdapter('App.Post', DS.RESTAdapter.extend({
url: "/rest/post/"
}));
Is this for literally just for one model across your entire app or is this the default "hasMany" uri that your REST backend uses? I ask because my api (django rest framework) uses this exact uri and it required a full blown pull request on the ember-data project because to build the URL the adapter needs the related "parent" or "owner" (something rails devs never needed so it didn't exist).
I would write your own adapter (just subclass the base adapter so you only override the single hasMany that is different). The method I wrote for my adapter is below and here is my full blown adapter for reference.
This is ember-data revision 11 friendly btw (have not upgraded to 12 yet)
https://github.com/toranb/ember-data-django-rest-adapter/blob/master/tests/adapter.js
findMany: function(store, type, ids, parent) {
var json = {}
, root = this.rootForType(type)
, plural = this.pluralize(root)
, ids = this.serializeIds(ids)
, url = this.buildFindManyUrlWithParent(store, type, ids, parent);
this.ajax(url, "GET", {
success: function(pre_json) {
json[plural] = pre_json;
Ember.run(this, function(){
this.didFindMany(store, type, json);
});
}
});
},