Feathersjs retrieve foreign key value through API - postgresql

I am using PostgresSQL database with Feathersjs, connecting both through Knex. The database table (NewTable) was created with a foreign key, referencing to the users services.
Following describe the database table.
module.exports = function (app) {
const db = app.get('knexClient');
db.schema.createTableIfNotExists('NewTable', table => {
table.increments('id').primary();
table.integer('ownerId').references('users.id');
})
I will receive the foreign key value of ownerId (in integer) when I query it through GET.
export function load(id) {
return {
types: [LOAD, LOAD_SUCCESS, LOAD_FAIL],
promise: ({ app }) => app.service('NewTable').get(id)
};
}
What is the proper way to retrieve the first_name column of the user instead of userId through the GET api?
Do I need to run another GET api within the hook of NewTable service to get the first_name column, or there is a better/easier way to do it?

Create a function like below within the hook of feathersjs.
function populateUser() {
return populate({
schema: {
include: [{
nameAs: 'sentBy',
service: 'users',
parentField: 'ownerId',
childField: 'id'
}]
}
});
}
Use it within the hook to call it after the GET. The columns of user service will be included within the response.
after: {
all: [],
find: [],
get: [
populateUser(),
}
],
create: [],
update: [],
patch: [],
remove: []
},

Related

Prisma Typescript where clause inside include?

I am trying to query the database (Postgres) through Prisma. My query is
const products = await prisma.products.findMany({
where: { category: ProductsCategoryEnum[category] },
include: {
vehicles: {
include: {
manufacturers: { name: { in: { manufacturers.map(item => `"${item}"`) } } },
},
},
},
});
The error message is
Type '{ name: { in: { manufacturers: string; "": any; }; }; }' is not assignable to type 'boolean | manufacturersArgs'.
Object literal may only specify known properties, and 'name' does not exist in type 'manufacturersArgs'.ts(2322)
Manufacturers have the field name and it is unique; I am not sure why this is not working or how I can update this code to be able to query the database. It is like I should cast the values into Prisma arguments.
The TypeScript error is pretty self-explanatory: the name property does not exist in manufacturersArgs. The emitted Prisma Client does a great job of telling you what properties do and do not exist when filtering.
If you are trying to perform a nested filter, you need to use select instead of include.
Documentation: https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#filter-a-list-of-relations
Your query is going to look something like this:
const products = await prisma.products.findMany({
where: { category: ProductsCategoryEnum[category] },
select: {
// also need to select any other fields you need here
vehicles: {
// Updated this
select: { manufacturers: true },
// Updated this to add an explicit "where" clause
where: {
manufacturers: { name: { in: { manufacturers.map(item => `"${item}"`) } } },
},
},
},
});
The final code ultimately depends on your Prisma schema. If you are using an editor like VS Code, it should provide Intellisense into the Prisma Client's TypeScript definitions. You can use that to navigate the full Prisma Client and construct your query based on exactly what is and is not available. In VS Code, hold control [Windows] or command [macOS] and click on findMany in prisma.products.findMany. This lets you browse the full Prisma Client and construct your query!
The in keyword isn't working for me. I use hasSome to find items in an array. hasEvery is also available depending what the requirements are.
hasSome: manufacturers.map(item => `"${item}"`),
See https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#scalar-list-filters

How do I write a Prisma findFirst where clause that OR's two AND's -- or how does prisma treat undefined's?

I am using Prisma
I have a postgres table I want to search, finding only the record that matches where (id and accessKey) or (id and ownerId)
It keeps finding where (id) if the accessKey or ownerId are undefined
I've found that if I use impossible strings then the search works
I can't find the appropriate answer in the prisma docs so I thought I'd ask here
return await db.attendee.findFirst({
where: {
OR: [
{ AND: { id, ownerId } },
{ AND: { id, accessKey } },
]
}
})
it makes sense that if accessKey or ownerId are undefined that they then effectively disappear from the query
I used this and it worked -- the values are guid's so they will never match, but if my secret phrase got loose (it's in the repo, not in an env var) then that would be an exploit akin to sql-injection...
const userId = context?.currentUser?.sub || `can't touch this`;
const accessKey = vaccessAuth?.accessKey || `can't touch this`;
What might be more the "prisma-way" ?
From what I understand, we can phrase your problem like this (please correct me if I'm wrong).
Find the first attendee where:
id field matches AND ( accessKey OR ownerId field matches)
Try this:
return await prisma.attendee.findFirst({
where: {
OR: [
{ ownerId: ownerIdValue },
{ accessKey: accessKeyValue }
],
AND: { id: idValue }
}
});

PG-Promise - "Property doesn't exist" error with skip function

I am having trouble figuring out how to utilize skip to skip undefined/null values. I keep getting Error: Property 'vehicle_id' doesn't exist. Is skip within column set and skip of upsertReplaceQuery() somehow conflicting with each other? How can I get it to work?
const vehicleColumnSet = new pgp.helpers.ColumnSet(
[
{ name: 'user_id' },
{
name: 'vehicle_id',
skip: (c) => !c.exists,
},
{ name: 'model_id', def: null },
],
{ table: 'vehicle' }
);
const upsertReplaceQuery = (data, columnSet, conflictField) => {
return `${pgp.helpers.insert(
data,
columnSet
)} ON CONFLICT(${conflictField}) DO UPDATE SET ${columnSet.assignColumns({
from: 'EXCLUDED',
skip: conflictField,
})}`;
};
const vehicleUpsertQuery = upsertReplaceQuery(
{
user_id,
model_id: vehicle_model,
},
vehicleColumnSet,
'user_id'
);
await task.none(vehicleUpsertQuery);
PostgreSQL has no support for any skip logic within its multi-row insert syntax.
And pg-promise documentation also tells you within skip description:
An override for skipping columns dynamically.
Used by methods update (for a single object) and sets, ignored by methods insert and values.
It is also ignored when conditional flag cnd is set.
At most, you can add such logic against a single-row insert, as shown here.

Knex seed and primary key sequence

I using knex.js for seedings and migrations. With code similar to following.
exports.down = function(knex, Promise) {
knex.schema.table('users', function(table) {
table.dropColumn('fullname')
})
}
exports.seed = function(knex, Promise) {
// Deletes ALL existing entries
return knex('users').del()
.then(function () {
// Inserts seed entries
return knex('users').insert([
{
id: 1,
email: 'nigel#email.com',
password: 'dorwssap'
},
{
id: 2,
email: 'nakaz#email.com',
password: 'password1'
},
{
id: 3
email: 'jaywon#email.com',
password: 'password123'
}
]);
});
};
But when I trying to insert new data into the table - autoincrement doesn't work until I update sequence manually. How could I fix this issue?
When data is inserted with explicit id given, postgresql does not read the value from id_sequence thus, the sequence will not be incremented on every insert.
You need to create data without giving ids explicitly or otherwise you need to update each table's id sequence to correct values after creating the initial data.

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