Return selected fields only React Native / GraphQL / Amplify - react-native

I have the following basic list query in GraphQL:
API.graphql(graphqlOperation(listSomething,{filter: filter})).then(({ data: { something } }) => {
// Returns the entire object
})
This will return an array containing entire objects, but as I've seen on multiple GraphQL tutorials, I'd like to take advantage of the utility of only returning / plucking select fields for obvious reasons. However there isn't a lot of information out there on GraphQL + RN + Amplify. So how would I re-write this query to only return the attribute id for instance? Or is that something that I have to define as a resolver in the graphql / queries.js file? I'm using autogenerated queries so it sort of feels like an anti-pattern to add your own queries to that file — Correct me if I'm wrong.
Thanks!

I'm still at an early stage of getting familiar with AWS, so please take this with a grain of salt.
Your auto generated queries.js will contain a listSomething query, that looks something like this:
export const listTodos = /* GraphQL */ `
query ListTodos(
$filter: ModelTodoFilterInput
$limit: Int
$nextToken: String
) {
listTodos(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
userID
name
description
createdAt
updatedAt
}
nextToken
}
}
`;
I created a new folder called /src/customGraphQL and created a file called customListTodos.js. I copied there the auto generated query, changed the query name, and removed the fields I didn't need.
export const customListTodos = /* GraphQL */ `
query CustomListTodos(
$filter: ModelTodoFilterInput
$limit: Int
$nextToken: String
) {
listTodos(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
name
description
}
nextToken
}
}
`;
I only changed the query's name, but kept the inner function's name as listTodos, so I'm running a customised version of that query. If the original API is pushed to AWS, you can import and use the custom query:
import {customListTodos} from './src/customGraphQL/customListTodos';
...
API.graphql(graphqlOperation(customListTodos ...

This worked for me. Making a custom queries file was pretty simple and I was able to limit the object to just returning the _version.
export const getUserVersion = /* GraphQL */ `
query GetUserVersion($id: ID!) {
getUser(id: $id) {
_version
}
}
`;
Then in my .js file I could call the function this way:
const getUserVersionResponse = await API.graphql(
graphqlOperation(getUserVersion, { id })
);

Related

Where do I execute native SQL queries in Loopback 4?

I'm new to Loopback 4 and I've been trying to execute a native SQL query. I found how to do it, the thing is that don't have any clue of WHERE to put it in my code in order for it to work... here's the documentation I found.
I need to know where should I put this:
const result = await repository.execute('SELECT * FROM Products');
in my Loopback project, which has many files. My goal is to make a REST endpoint like /products/[name] with [name] being a parameter inserted dynamically to the SQL query.
You can do it in your controller class as per loopback docs https://loopback.io/doc/en/lb4/Controller.html. As you will define the REST endpoint in the controller itself you can also do the insertion there using repository.execute() e.g.
#get('/products/{name}')
async doSomething(
#param.path.string('name') name: string,
): Promise<Product> {
const sql = `SELECT * FROM some_table WHERE some_field="${name}"`;
await this.productRepository.execute(sql)
--- other lines of code & return value --
}
Personally, I would implement it as a new Repository method.
Let's say your model is called Product, then you should have src/repositories/product.repository.ts file exporting ProductRepository class already present in your project. (You can run lb4 repository to create it.)
export class Product extends DefaultCrudRepository<
Product,
typeof Product,
Product Relations
> {
constructor(#inject('datasources.db') dataSource: DbDataSource) {
super(Product, dataSource);
}
// your custom method
async selectByName(name: string): Promise<Product[]> {
const rawItems = await repository.execute('SELECT * FROM Products');
// would you like to convert raw data into Product instances?
return rawItems.map(it => new Product(it));
}
}
Then you can call this new custom repository method from your controller in the same way as you would call e.g. repository.find(filter).

How can I use the same value as written in the Json during the same test execution in the testcafe

I have been trying to use the value from the JSON that I have got added successfully using fs.write() function,
There are two test cases in the same fixture, one to create an ID and 2nd to use that id. I can wrote the id successfully in the json file using fs.write() function and trying to use that id using importing json file like var myid=require('../../resources/id.json')
The json file storing correct id of the current execution but I get the id of first test execution in 2nd execution.
For example, id:1234 is stored during first test execution and id:4567 is stored in 2nd test execution. During 2nd test execution I need the id:4567 but I get 1234 this is weird, isn't it?
I use it like
t.typeText(ele, myid.orid)
my json file contains only id like {"orid":"4567"}
I am new to Javascript and Testcafe any help would really be appreciated
Write File class
const fs = require('fs')
const baseClass =require('../component/base')
class WriteIntoFile{
constructor(orderID){
const OID = {
orderid: orderID
}
const jsonString = JSON.stringify(OID)
fs.writeFile(`resources\id.json`, jsonString, err => {
if (err) {
console.log('Error writing file', err)
} else {
console.log('Successfully wrote file')
}
})
}
}
export default WriteIntoFile
I created 2 different classes in order to separate create & update operations and call the functions of create & update order in single fixture in test file
Create Order class
class CreateOrder{
----
----
----
async createNewOrder(){
//get text of created ordder and saved order id in to the json file
-----
-----
-----
const orId= await baseclass.getOrderId();
new WriteIntoFile(orId)
console.log(orId)
-----
-----
-----
}
}export default CreateOrder
Update Order class
var id=require('../../resources/id.json')
class UpdateOrder{
async searchOrderToUpdate(){
await t
***//Here, I get old order id that was saved during previous execution***
.typeText(baseClass.searchBox, id.orderid)
.wait(2500)
.click(baseClass.searchIcon)
.doubleClick(baseClass.orderAGgrid)
console.log(id.ordderid)
----
----
async updateOrder(){
this.searchOrderToUpdate()
.typeText(baseClass.phNo, '1234567890')
.click(baseClass.saveBtn)
}
}export default UpdateOrder
Test file
const newOrder = new CreateOrder();
const update = new UpdateOrder();
const role = Role(`siteurl`, async t => {
await t
login('id')
await t
.wait(1500)
},{preserveUrl:true})
test('Should be able to create an Order', async t=>{
await newOrder.createNewOrder();
});
test('Should be able to update an order', async t=>{
await update.updateOrder();
});
I'll reply to this, but you probably won't be happy with my answer, because I wouldn't go down this same path as you proposed in your code.
I can see a couple of problems. Some of them might not be problems right now, but in a month, you could struggle with this.
1/ You are creating separate test cases that are dependent on each other.
This is a problem because of these reasons:
what if Should be able to create an Order doesn't run? or what if it fails? then Should be able to update an order fails as well, and this information is useless, because it wasn't the update operation that failed, but the fact that you didn't meet all preconditions for the test case
how do you make sure Should be able to create an Order always runs before hould be able to update an order? There's no way! You can do it like this when one comes before the other and I think it will work, but in some time you decide to move one test somewhere else and you are in trouble and you'll spend hours debugging it. You have prepared a trap for yourself. I wrote this answer on this very topic, you can read it.
you can't run the tests in parallel
when I read your test file, there's no visible hint that the tests are dependent on each other. Therefore as a stranger to your code, I could easily mess things up because I have no way of knowing about it without going deeper in the code. This is a big trap for anyone who might come to your code after you. Don't do this to your colleagues.
2/ Working with files when all you need to do is pass a value around is too cumbersome.
I really don't see a reason why you need to same the id into a file. A slightly better approach (still violating 1/) could be:
const newOrder = new CreateOrder();
const update = new UpdateOrder();
// use a variable to pass the orderId around
// it's also visible that the tests are dependent on each other
let orderId = undefined;
const role = Role(`siteurl`, async t => {
// some steps, I omit this for better readability
}, {preserveUrl: true})
test('Should be able to create an Order', async t=>{
orderId = await newOrder.createNewOrder();
});
test('Should be able to update an order', async t=>{
await update.updateOrder(orderId);
});
Doing it like this also slightly remedies what I wrote in 1/, that is that it's not visible at first sight that the tests are dependent on each other. Now, this is a bit improved.
Some other approaches how you can pass data around are mentioned here and here.
Perhaps even a better approach is to use t.fixtureCtx object:
const newOrder = new CreateOrder();
const update = new UpdateOrder();
const role = Role(`siteurl`, async t => {
// some steps, I omit this for better readability
}, {preserveUrl:true})
test('Should be able to create an Order', async t=>{
t.fixtureCtx.orderId = await newOrder.createNewOrder();
});
test('Should be able to update an order', async t=>{
await update.updateOrder(t.fixtureCtx.orderId);
});
Again, I can at least see the tests are dependent on each other. That's already a big victory.
Now back to your question:
During 2nd test execution I need the id:4567 but I get 1234 this is weird, isn't it?
No, it's not weird. You required the file:
var id = require('../../resources/id.json')
and so it's loaded once and if you write into the file later, you won't read the new content unless you read the file again. require() is a function in Node to load modules, and it makes sense to load them once.
This demonstrates the problem:
const idFile = require('./id.json');
const fs = require('fs');
console.log(idFile); // { id: 5 }
const newId = {
'id': 7
};
fs.writeFileSync('id.json', JSON.stringify(newId));
// it's been loaded once, you won't get any other value here
console.log(idFile); // { id: 5 }
What you can do to solve the problem?
You can use fs.readFileSync():
const idFile = require('./id.json');
const fs = require('fs');
console.log(idFile); // { id: 5 }
const newId = {
'id': 7
};
fs.writeFileSync('id.json', JSON.stringify(newId));
// you need to read the file again and parse its content
const newContent = JSON.parse(fs.readFileSync('id.json'));
console.log(newContent); // { id: 7 }
And this is what I warned you against in the comment section. That this is too cumbersome, inefficient, because you write to a file and then read from the file just to get one value.
What you created is not very readable either:
const fs = require('fs')
const baseClass =require('../component/base')
class WriteIntoFile{
constructor(orderID){
const OID = {
orderid: orderID
}
const jsonString = JSON.stringify(OID)
fs.writeFile(`resources\id.json`, jsonString, err => {
if (err) {
console.log('Error writing file', err)
} else {
console.log('Successfully wrote file')
}
})
}
}
export default WriteIntoFile
All these operations for writing into a file are in a constructor, but a constructor is not the best place for all this. Ideally you have only variable assignments in it. I also don't see much reason for why you need to create a new class when you are doing only two operations that can easily fit on one line of code:
fs.writeFileSync('orderId.json', JSON.stringify({ orderid: orderId }));
Keep it as simple as possible. it's more readable like so than having to go to a separate file with the class and decypher what it does there.

Dexie.js table.name isn't working even though the table is under the tables property

I'm wanting to fetch all items from a table into a collection but am getting an error that the table name is undefined. Here is my store:
db.version(1).stores({
users: '++id,',
orgs: '++id,',
applications: '++id'
})
Then later here is my call:
db.orgs.toCollection().count(function (count) {
console.log(count)
})
It gives the following error:
TypeError: Cannot read property 'toCollection' of undefined
But when I stop the debugger at the call and type in db.tables sure enough:
1:Table {name: "orgs", schema: TableSchema, _tx: undefined, …}
_tx:undefined
hook:function rv(eventName, subscriber) { … }
name:"orgs"
Any help is appreciated - thanks.
UPDATE
I noticed that when I seeded the database on initial creation I could fetch the data out. So I copied in that code into my template. It still fails however, so there must be something simple I'm missing, here is that code:
import Dexie from '#/dexie.es.js'
export default {
name: 'ListOrgs',
data: () => {
return {
orgs: []
}
},
methods: {
populateOrgs: async function () {
let db = await new Dexie('myDatabase').open()
db.orgs.toCollection().count(function (count) {
console.log(count)
})
}
},
mounted () {
this.populateOrgs()
}
}
Dexie has two modes
Static - the most common one used in most samples.
Dynamic - Schema is not specified in code.
Static Mode
//
// Static Mode
//
const db = new Dexie('myDatabase');
db.version(1).stores({myTable1: '++'});
db.version(2).stores({myTable1: '++, foo'});
db.myTable1.add({foo: 'bar'}); // OK - dexie knows about myTable1!
Dynamic Mode
//
// Dynamic Mode
//
const db = new Dexie('myDatabase');
// FAIL: db.myTable1.add({foo: 'bar'}); // myTable1 is unknown to the API.
// Here, you must wait for db to open, and then access tables using db.table() method:
db.open().then(db => {
const myTable = db.table('myTable');
if (myTable) {
myTable.add({foo: 'bar'});
}
}).catch(error => {
console.error(error);
});
If omitting any version() specification, Dexie will just try to open any existing database with the same name, no matter version or schema. But it won't create the implicit table properties onto the db instance.
When Dynamic Mode is Useful
Dynamic mode can be useful when building an arbritary database utility that should adapt to any indexedDB database - such as a DB explorer. Dynamic mode can also be useful when the javascript code is by design not aware of the schema (what tables are expected to be queried and what indexes there are).
Benefits with Static Mode
No need to wait for db.open() to complete.
Automatic DB creation when neeeded. No complex app code to deal with database versioning.
Automatic DB population when needed.
Design Patterns in Static Mode
db.js
import Dexie from 'dexie';
//
// Let this module do several things:
//
// * Create the singleton Dexie instance for your application.
// * Declare it's schema (and version history / migrations)
// * (Populate default data http://dexie.org/docs/Dexie/Dexie.on.populate)
//
export const db = new Dexie('myDatabase');
db.version(1).stores({
users: '++id,',
orgs: '++id,',
applications: '++id'
});
db.on('populate', () => {
return db.orgs.bulkAdd([
{'foo': 'bar'},
]);
});
app.js
import {db} from './db';
// Wherever you use the database, include your own db module
// instead of creating a new Dexie(). This way your code will
// always make sure to create or upgrade your database whichever
// of your modules that comes first in accessing the database.
//
// You will not have to take care of creation or upgrading scenarios.
//
// Let Dexie do that for you instead.
//
async function countOrgs() {
return await db.orgs.count();
}

GraphQL gql Syntax Error: Expected Name, found }

I'm attempting to set up Apollo GraphQL support in a new React project, but when I try to compile a query using gql I keep receiving the error:
Syntax Error: Expected Name, found }
This is generated by the following code:
import gql from 'graphql-tag'
const query = gql`
{
user(id: 5) {
firstName
lastName
}
}
`
console.log(query)
I'm basing this code off the example code found here: https://github.com/apollographql/graphql-tag
What is the Name referred to in the error message? Does anyone know what I'm doing wrong here?
This error occurs mostly when there are unclosed curly braces or when some fields are not properly defined while calling the query.
The accepted answer didn't solve my issue. Instead, it worked if you remove the initial curly brackets.
The query should look like this instead:
const query=gql`
user(id: 5) {
firstName
lastName
}
`
The causes could be:
you are adding a "()" at the beginning for no reason
you need to add more 'nested' parameters.
Especially if you are using an online GraphiQL editor. Examples:
1- Wrong code (extra parenthesis)
{
allFilms() {
films {
title
}
}
}
2- Wrong code (more parameters need it eg: title)
{
allFilms {
films {
}
}
}
3- Correct code
{
allFilms {
films {
title
}
}
}
GraphQLError: Syntax Error: Expected Name, found "$".
One more example of a similar error (For other users).
theErrorIsHere (Could be extra ( or { before the $varName) added before $speakerId
Error code:
const FEATURED_SPEAKER = gql`
mutation markFeatured($speakerId: ID!, $featured: Boolean!){
markFeatured(speaker_id: theErrorIsHere$speakerId , featured: $featured){
id
featured
}
}
`;
Correct code:
const FEATURED_SPEAKER = gql`
mutation markFeatured($speakerId: ID!, $featured: Boolean!){
markFeatured(speaker_id: $speakerId , featured: $featured){
id
featured
}
}
`;
I'm not 100% sure what the root of my problem was, but moving all the query code into a separate es6 module fixed the issue. There must have been some kind of contamination from the surrounding code. For reference my query was embedded within a React component.
This works:
import gql from 'graphql-tag'
const query = gql`
{
user(id: 5) {
firstName
lastName
}
}
`
export default query
Another cause for this error: you are referencing a type that is defined further down. Move the type you are referencing up.
For example:
type Launch {
rocket: Rocket
}
type Rocket {
name: String
}
will throw an error, as Launch references Rocket before Rocket is defined.
The corrected code:
type Rocket {
name: String
}
type Launch {
rocket: Rocket
}
In my case, I got the error simply because I'm adding : which I shouldn't have done.
e.g:
const query = `
query($id: String!) {
getUser(id: $id) {
user: {
id
name
email
createdAt
}
}
}
`
If you pay close attention to line 4 of the code above you'll realize that I added : after the user before the curly brace, then I began to list the user's data I wanna query and THAT WAS EXACTLY WHERE THE ERROR WAS!
Removing the : solve the issue!
It should be:
user {
id
name
...
}
In NestJS framework, this error happened to me because I defiled GraphQL field in my schema.graphql file as:
lastUpdated(): Date
Instead it should be just
lastUpdated: Date
(it doesn't take any argument)
I was receiving a similar error server side:
GraphQLError: Syntax Error: Expected Name, found ]
I realized the cause in my case was a type definition with an empty array.
This breaks:
type Settings {
requires: []
}
But this works:
type Settings {
requires: [String]
}
I had this problem and the cause was a string value with double-quotes inside double-quotes, like so: "this "is" bad".
In my case I got the error because of the following:
const GET_POSTS_OF_AUTHOR = gql`
query GetPostsOfAuthor($authorId: Int!) {
postsOf($authorId: Int!) {
id
title
}
}
`;
When it should have been:
const GET_POSTS_OF_AUTHOR = gql`
query GetPostsOfAuthor($authorId: Int!) {
postsOf(authorId: $authorId) {
id
title
}
}
`;
erroneously thought $authorId passed through identically to the function call instead of setting a property inside the function call.
This can happen if you use gql from #clinet/apollo and in the backticks you try to inject dynamic js value. Remove it and replace with normal scalar and it will fix your issue.
example:
${SOME_MAX_VALUE} -> 20
On ny side the error was caused by extra {} Curly braces. Solved by just removing them.
I was getting the same error. In my case putting the id inside double quote solved the issue as the type of id required string value.
{
product(id: "${id}") {
name
}
}
Posting here in case anyone else had this problem but you also get this error if you accidentally make your query look like json with colons (:).
ex:
data {
property {
key: {
deepKey
}
}
}
will give the same error from GQL compile

Remove `data` key from fractal transformed single item

I'm using fractal with lumen framework to build an API. It works great but when I return any specific item, it return the result inside a data key.
{ data : { /** All data **/ }}
I understand the use of data key in a collection. But I don't feel the necessity of having data key in single result. ( Correct me if its a wrong REST convention )
So how can I remove data key from single result?
Put this code in your Bootstrap/app.phpcan help you to avoid data. You can make it as a service provider also.
$app->bind('League\Fractal\Manager', function ($app) {
$fractal = new \League\Fractal\Manager;
$serializer = new \League\Fractal\Serializer\ArraySerializer();
$fractal->setSerializer($serializer);
return $fractal;
});
$app->bind('Dingo\Api\Transformer\Adapter\Fractal', function ($app) {
$fractal = $app->make('\League\Fractal\Manager');
return new \Dingo\Api\Transformer\Adapter\Fractal($fractal);
});