Building a react-admin data provider for FaunaDB - react-admin

With Graphcool being sunsetted, I've been trying to get react-admin to work with Fauna, but have not been successful. The Fauna GraphQL grammar is different than Graphcool, and of course when I connect to my Fauna DB, it doesn't recognize any of the resources.
If I build a very simple Fauna DB by uploading the following GraphQL schema:
type Location {
name: String!
address1: String!
address2: String
city: String
state: String
zip: String
}
type Query {
allLocations: [ Location ]
}
The final schema that Fauna creates looks like:
directive #embedded on OBJECT
directive #collection(name: String!) on OBJECT
directive #index(name: String!) on FIELD_DEFINITION
directive #resolver(
name: String
paginated: Boolean! = false
) on FIELD_DEFINITION
directive #relation(name: String) on FIELD_DEFINITION
directive #unique(index: String) on FIELD_DEFINITION
scalar Date
type Location {
city: String
name: String!
zip: String
state: String
_id: ID!
address2: String
address1: String!
_ts: Long!
}
input LocationInput {
name: String!
address1: String!
address2: String
city: String
state: String
zip: String
}
type LocationPage {
data: [Location]!
after: String
before: String
}
scalar Long
type Mutation {
createLocation(data: LocationInput!): Location!
updateLocation(
id: ID!
data: LocationInput!
): Location
deleteLocation(id: ID!): Location
}
type Query {
findLocationByID(id: ID!): Location
allLocations(
_size: Int
_cursor: String
): LocationPage!
}
scalar Time
I'm having a hard time figuring out how to adapt the Graphcool/GraphQL data providers to account for the difference in the Fauna grammar. A couple of differences I've noticed:
Fauna names the IDs _id, but ra-data-graphcool expects them to be id
Fauna create/update mutations expect a single input object that mirrors the type
There are probably also differences in how the Graphcool data provider builds out the gql for getting record lists as well. Has anyone created or modified a data provider for Fauna (or maybe Back4App or other free/inexpensive cloud GraphQL providers)?

Related

JSON Object validation with JSONSchema Validator

const Validator = require('jsonschema').Validator;
const validator = new Validator();
const obj =
[
{
"id":"1",
"firstname":"Jack"
}
];
const instance= {
properties: {
id: {
type: 'number'
},
firstname: {
type: 'string'
}
},
required: ['id', 'firstname'],
additionalProperties: false
};
const result = validator.validate(obj, instance);
console.log(result.errors);
I want to validate a JSON Object using jsonschema Validator. when json object is not as per schema, then also validate function is not returning any error. irrespective of obj being as per schema/instance or not, its error section always returning null.
You've defined obj as an array as opposed to an object. As a result, validation is passing because you've used JSON Schema keywords which only apply to objects. (Many JSON Schema keywords are only applicable to specific types.)
In your schema, add "type": "object", and you should see an error when the instance is not an object.
On a side note, an "instance" is the data you want to validate, and a "schema" is the JSON Schema you want to use for validation.

GraphQL - Parameters not being passed to backend in mutation

I've just started with GraphQL and mutations and am having this issue.
When I attempt to pass data to my GraphQL server, it seems the args parameter does not register the args that I am attempting to pass.
When I console.log them, they appear as undefined, but the method is working and receiving the request.
Does anything seem incorrect below? I'm just getting my head around it so would appreciate any feedback.
This is the query I'm attempting to run from GraphiQL
mutation {
addDelivery(
carrier: "AusPost",
goodsDelivered:[],
receiver: {
first_name:"ll",
last_name:"test",
receiver_email: "bbb#bbb.com.au"
},
consignmentNumber:"AAAA000",
poNumber:"PO0000"
) {
id
carrier {
iD
carrier_name
}
}
}
My schema:
type ReceiverType{
first_name: String
last_name: String
receiver_email: String
}
type ItemType {
item_type: String,
quantity: String
}
type Deliveries {
carrier: Carrier
consignmentNumber: String
goodsDelivered: [String]
id: ID
items: [ItemType]
poNumber: String
receiver: ReceiverType
}
type Carrier{
iD: ID
carrier_name: String
}
input Item {
item_type: String,
quantity: String
}
type Query {
deliveries: [Deliveries]
}
input Receiver {
first_name: String
last_name: String
receiver_email: String
}
type Mutation {
addDelivery(
carrier: String!,
consignmentNumber: String!,
goodsDelivered: [Item]!,
poNumber: String!,
receiver: [Receiver]!
) : Deliveries
}
My add delivery resolver
addDelivery: async (parent, args, context, info) => {
const delivery = new Deliveries({
receiver: {
first_name: args.first_name,
last_name: args.last_name,
receiver_email: args.receiver_email
},
items: args.items,
consignment_no: args.consignment_no,
po_number: args.po_number,
carrier_id: args.carrier_id,
delivery_id: args.delivery_id
});
await delivery.save();
return delivery;
}
I changed the signature to the following and it resolved the issue.
addDelivery: async ({receiver, items, consignment_no, po_number, carrier_id, delivery_id}) => {

How To Use The Same GraphQL Query Multiple Times In A Single API Call. Amplify, DynamoDB

The user will input multiple usernames and I would like to search my DynamoDB table for all each user's id in a single API call.
The following is the important portion of the graphql user schema:
type User
#model
#key(name: "byName", fields: ["username"], queryField: "findUsername")
{
id: ID!
email: AWSEmail!
username: String!
firstName: String
lastName: String
createdAt: AWSDateTime
updatedAt: AWSDateTime
}
I would like to create a query which takes the list of usernames and returns each user's user id in a single api call. I am not sure how to dynamically and separately add the usernames to a query. Btw, I am using aws-amplify to help with dynamodb and graphql. This is also a React Native project.
You can run multiple queries and mutations using batch operations in AWS AppSync. Eg: BatchGetItem
type Post {
id: ID!
title: String
}
type Query {
batchGet(ids: [ID]): [Post]
}
Sample query
query get {
batchGet(ids:[1,2,3]){
id
title
}
}
batchGet.request.vtl
#set($ids = [])
#foreach($id in ${ctx.args.ids})
#set($map = {})
$util.qr($map.put("id", $util.dynamodb.toString($id)))
$util.qr($ids.add($map))
#end
{
"version" : "2018-05-29",
"operation" : "BatchGetItem",
"tables" : {
"Posts": {
"keys": $util.toJson($ids),
"consistentRead": true
}
}
}
batchGet.response.vtl
$util.toJson($ctx.result.data.Posts)
For more details check out this tutorial.
https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-dynamodb-batch.html

How to query by non-primary key in AWS-amplify graphql schema

I have a graphql schema in aws-amplify, appsync.
my schema is:
type Project
#model
{
id: ID!
project_number: String!
name: String!
architect: String!
interfaces: [Interface] #connection(name: "ProjectInterfaces")
}
type Interface
#model
{
id: ID!
interface_name: String!
version: String!
release: String!
source_feature: String!
MFT_feature_id: String!
state: String!
source_application: String!
source_env: String!
source_go_live: String!
source_payload: String!
source_payload_format: String!
source_payload_volume: String!
source_protocol: String!
target_application: String!
target_env: String!
target_go_live: String!
target_payload: String!
target_payload_format: String!
target_payload_volume: String!
target_protocol: String!
frequency: String!
authentication: String!
payload_security: String!
transport_security: String!
network_paths: String!
project: Project #connection(name: "ProjectInterfaces")
}
and I want to know if I can search a projects interfaces by project_number instead of id. I added an index on project_name, but it doesn't seem to work. My current query is
query GetProject {
getProject(project_number:"P200") {
name
architect
interfaces{
items{
interface_name
version
release
source_feature
MFT_feature_id
state
source_application
source_env
source_go_live
source_payload
source_payload_format
source_payload_volume
source_protocol
target_application
target_env
target_go_live
target_payload
target_payload_format
target_payload_volume
target_protocol
frequency
authentication
payload_security
transport_security
network_paths
}
}
}
}
Please let me know if this is possible. I would like it so that I can just search by project_name, and then get all the details from interface.
So according to the Amplify docs you would have to add an #index on the field you'd like to query on. I've updated your Project model to have an index on the project_number field.
type Project
#model
{
id: ID!
project_number: String! #index(name: "byProjectName", queryField: "projectByNumber", sortKeyFields: ["id"])
name: String!
architect: String!
interfaces: [Interface] #connection(name: "ProjectInterfaces")
}
Then to query by project_number, use the query name set in the queryField (projectByNumber) in the index:
query projectByNumber($projectNumber: String!) {
projectByNumber(project_number: $projectNumber) {
items {
id
project_number
name
architect
...
}
}
}
Please checkout the docs under Query employee details by employee name to get a better understanding: https://docs.amplify.aws/cli/graphql/examples-and-solutions/#2-query-employee-details-by-employee-name

How to manage data template when serving an api using Node.js

I'm currently trying to create an api for my web app using Node.js. The api intended to return JSON data to the api user.
For example, i'm having following endpoint / object:
- Articles (collection of article)
- Article (single article, each article would be having tags)
- Tags
Each endpoint / object having they own json data format need to return, in example:
Articles: [{articleObject}, {articleObject}, {articleObject}]
Article
{
id: 12,
title: 'My awesome article *i said and i know it',
content: 'blah blah blah blah',
tags: [{tagObject}, {tagObject}, {tagObject}]
}
Each endpoint can call to other endpoint to get needed data, in example:
Article endpoint can calling the Tags endpoint to get the article tags collection, the Tags endpoint will be returning Array Object.
My questions:
How to manage the Object structures, so if a endpoint called from another endpoint it will return Array / Object, while it will be return JSON string when called from api user.
Should I create Object template for each endpoint. The process will be:
Endpoint will be calling Object Template;
The Object Template will fetching data from database;
Endpoint will return the Object Template when called from other endpoint;
Endpoint will call template engine to parse the Object Template to JSON string for api user. JSON string view will be provided for each endpoint.
What Template Engine can process below Object Template and treat them as variables, in PHP i'm using Twig that can receive PHP Object and treat them as variables. Here you can find how i do that using Twig
Object Template file content example:
var Tags = require('./tags');
module.exports = function(param){
/* do fetching data from database for specified parameters */
/* here */
var data = {};
return {
id: function() {
return data.id;
},
title: function() {
return data.title;
},
description: function() {
return data.description;
},
tags: function() {
return Tags(data.id);
}
}
}
Hope someone can show me the way for it.
I use express to do my apis...you can do something like this:
app.get('/api/article', authenticate, routes.api.article.get);
authenticate is just a middleware function that authenticates the user, typically this is grabbing the user from session and making sure they are logged in.
inside ./routes/api.js
var Article = require('../models/article.js');
exports.api = {}
exports.api.article = {}
exports.api.article.get = function(req, res){
//get article data here
Article.find({created_by: req.session.user._id}).populate('tags').populate('created_by').exec(function(err, list){
res.json(list);
});
};
This assume you're using mongoose and mongodb and have an Article schema similar to this, which includes a reference to a Tag schema:
var ArticleSchema = new Schema({
name : { type: String, required: true, trim: true }
, description: { type: String, trim: true }
, tags : [{ type: Schema.ObjectId, ref: 'Tag', index: true }]
, created_by : { type: Schema.ObjectId, ref: 'User', index: true }
, created_at : { type: Date }
, updated_at : { type: Date }
});