AWS AppSync Lambda Resolver Get Query Return Type - aws-appsync

In AWS AppSync, I have a Lambda resolver. The resolver handles multiple queries e.g. getUser, getUsers, getClient, etc. How can the Lambda function get the query return type defined in the AppSync GraphQL schema?
The return types are defined in the AppSync GraphQL schema query section:
type Query {
getClient(_id: ID!): Client
getUser(_id: ID!): User
getUsers(): UserConnection
}
The lambda event variables contain the following for the query getUser:
"info": {
"parentTypeName": "Query",
"selectionSetList": [
"_id",
"email"
],
"selectionSetGraphQL": "{\n _id\n email\n}",
"fieldName": "getUser",
"variables": {
"id": "5c42109b2eb8ed82c8862532"
}
},
"stash": {}
Using the AWS SDK I can call the AppSync API method getIntrospectionSchema but it does not return any queries or mutations -- only an array of all types.
I can also call the getResolver method but it does not return the field return type.
var appsync = new AWS.AppSync();
var params = {
apiId: 'xxxxx',
fieldName: 'getUser',
typeName: 'Query',
};
const data = await appsync.getResolver(params).promise();
Response:
dataSourceName:'myLambdaDatasource'
fieldName:'getUser'
kind:'UNIT'
requestMappingTemplate:null
resolverArn:'arn:.../types/Query/resolvers/getUser'
responseMappingTemplate:null
typeName:'Query'

As a workaround (and because I'm using the AWS CDK to create the AppSync API) I can inject variables into the datasource Lambda function via a pipeline resolver request mapping template. It's not ideal because we need to mimic the variables sent to a direct Lambda resolver (which could change). For now, this VTL works: $util.toJson($ctx)
api.addQuery(`get${objectTypeName}`, new ResolvableField({
returnType: objectType.attribute(),
args: { id: appsync.GraphqlType.id({ isRequired: true }) },
dataSource,
pipelineConfig: [],
requestMappingTemplate: appsync.MappingTemplate.fromString(`
$util.qr($ctx.stash.put("myVariable", "myValue"))
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": $util.toJson($ctx)
}
`)
}));
Variables are available in the Lambda event parameter i.e. event.stash.myVariable
"stash": {
"myVariable": "myValue"
}

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

Proxy request to graphql server through AWS appsync

I have a graphql server on one of my EC2 instances running. I also have AWS appsync running, but at the moment it's integrated only with couple of lambdas.
I'd like to connect my Appsync with graphql server, so Appsync will behave as a proxy for specific queries/mutations.
So from client side, it will look like this:
Client sends a query to the appsync, lets say that it looks like that:
{
user {
id
}
}
Appsync has defined a user query, it's configured to proxy the query the graphql server, without any changes
Graphql server is able to handle following query:
{
user {
id
}
}
and returns a result:
"data": {
"user": {
"id": "123456789"
}
}
finally Appsync proxies response back to the client
Am I able to configure Appsync in a way that given scenario is possible? Is it the right pattern to use Appsync for?
Update 1. After #mparis response
I was able to proxy my request through AppSync, and reach my graphql server, with following resolver configuration:
{
"version": "2018-05-29",
"method": "POST",
"resourcePath": "/graphql",
"params":{
"body": {
"query": "$util.escapeJavaScript($ctx.info.getSelectionSetGraphQL())"
}
}
}
But this still does not work as expected - it's different from description in docs, and I see at least two issues:
If I have a query with arguments and nested paylod, $ctx.info.getSelectionSetGraphQL() cuts out the query name and arguments part, and gives me only nested payload, so this query:
{
user(id: "1") {
picture {
url
}
}
becomes following one, once I call $ctx.info.getSelectionSetGraphQL():
{
picture {
url
}
}
But I'd like to use whole query, same as described in docs:
"selectionSetGraphQL": "{\n getPost(id: $postId) {\n postId\n title\n secondTitle: title\n content\n author(id: $authorId) {\n authorId\n name\n }\n secondAuthor(id: \"789\") {\n authorId\n }\n ... on Post {\n inlineFrag: comments {\n id\n }\n }\n ... postFrag\n }\n}"
Lets say I have a query, and I've defined resolver for user query in appsync, the query looks like this:
{
user(id: "1") {
picture {
url
}
}
and I call my graphql backend through appsync, in my graphql logs I see following response logged:
{
"data: {
"user": {
"picture": {
"url": "example.com/link/to/a/picture"
}
}
}
}
but appsync returns me given response instead:
{
"data: {
"user": {
"picture": null
}
}
}
I've figured out, that appsync expects from me that I will define resolvers for both user and picture query. Appsync wants to first call user and then picture query, but what I wanna do is simply proxy the response through appsync, without calling any additional queries except user. Everything is evaluated in my graphql backend and should be just put to the response body, but appsync wants to evaluate everything one more time.
Is there any solution for no 1. and 2.?
As of writing, you can use the $ctx.info object to get the selection set of queries from within the resolvers and send the relevant data via an HTTP resolver to your downstream service. Go here and look for the info field on the $ctx object. To make this work, you will need to mirror the schema of your downstream API within your AppSync API.
Thanks for bringing this up. The team is aware of these use cases and this helps prioritization.
You can use the following resolver function:
I used the openly available StarWarsAPI as data source: https://swapi-graphql.netlify.app
{
"version": "2018-05-29",
"method": "POST",
"resourcePath": "/.netlify/functions/index",
"params": {
"headers": {
"Content-Type": "application/json"
},
"body": {
"query": "query { starship (id: \"$ctx.args.id\") $util.escapeJavaScript($ctx.info.getSelectionSetGraphQL()) }"
}
}
}
And then you have to get the data from the graphql response using this reponse mapping:
$util.toJson($util.parseJson($ctx.result.body).data.starship)
(You have to duplicate the schema in appsync)

Compound queries

I have a RESTful service that accepts a custom query, like this:
/entities/User?actions=
{
"$link": {
"entityType": "Manager",
"entity": {
"name": "John Smith"
},
"linkName": "managers",
"backLinkName": "account",
"$set": {
"propertyName": "aclWrite",
"propertyValue": {
"$ref": {
"propertyName": "entityId"
}
}
}
}
}
Which simply means:
Create a new Entity of type User
Create a new Entity of type Manager with the field name, linking the User to be created to this Manager through link name "managers"
Then back-linking the Manager entity to be created to the User with a link name "account" and setting the Manager entity write ACL (Access Control List) to the ID of the User to be created.
I created this query structure because I can't find any suitable Query language that can seem to support such action/procedure.
The question here is are there any Query language that can support such compound action/procedure or can GraphQL handle such?
As a specification, GraphQL doesn't care what fields your schema defines, what arguments those fields take or what your field resolvers do with those arguments. So it's perfectly feasible to design a schema that would let the client compose an equivalent mutation:
mutation {
link(
entityType: "Manager"
entity: {
name: "John Smith"
}
linkName: "managers"
backLinkName: "account"
set: {
propertyName: "aclWrite"
propertyValue: {
ref: {
propertyName: "entityId"
}
}
}
) {
# some fields here to return in the response
}
}
GraphQL does not support references to other nodes inside the same query, so you would still probably want a single mutation whose input mirrored your existing API. That said, using GraphQL for this may still be preferable because of request validation, which is all the more important with complex requests like this. Having an IDE like GraphiQL or GraphQL Playground that lets you write your queries using autocomplete is a big plus too.

must have a selection of subfields. Did you mean \"createEvent { ... }\"?", [graphql] [duplicate]

Hi I am trying to learn GraphQL language. I have below snippet of code.
// Welcome to Launchpad!
// Log in to edit and save pads, run queries in GraphiQL on the right.
// Click "Download" above to get a zip with a standalone Node.js server.
// See docs and examples at https://github.com/apollographql/awesome-launchpad
// graphql-tools combines a schema string with resolvers.
import { makeExecutableSchema } from 'graphql-tools';
// Construct a schema, using GraphQL schema language
const typeDefs = `
type User {
name: String!
age: Int!
}
type Query {
me: User
}
`;
const user = { name: 'Williams', age: 26};
// Provide resolver functions for your schema fields
const resolvers = {
Query: {
me: (root, args, context) => {
return user;
},
},
};
// Required: Export the GraphQL.js schema object as "schema"
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
// Optional: Export a function to get context from the request. It accepts two
// parameters - headers (lowercased http headers) and secrets (secrets defined
// in secrets section). It must return an object (or a promise resolving to it).
export function context(headers, secrets) {
return {
headers,
secrets,
};
};
// Optional: Export a root value to be passed during execution
// export const rootValue = {};
// Optional: Export a root function, that returns root to be passed
// during execution, accepting headers and secrets. It can return a
// promise. rootFunction takes precedence over rootValue.
// export function rootFunction(headers, secrets) {
// return {
// headers,
// secrets,
// };
// };
Request:
{
me
}
Response:
{
"errors": [
{
"message": "Field \"me\" of type \"User\" must have a selection of subfields. Did you mean \"me { ... }\"?",
"locations": [
{
"line": 4,
"column": 3
}
]
}
]
}
Does anyone know what I am doing wrong ? How to fix it ?
From the docs:
A GraphQL object type has a name and fields, but at some point those
fields have to resolve to some concrete data. That's where the scalar
types come in: they represent the leaves of the query.
GraphQL requires that you construct your queries in a way that only returns concrete data. Each field has to ultimately resolve to one or more scalars (or enums). That means you cannot just request a field that resolves to a type without also indicating which fields of that type you want to get back.
That's what the error message you received is telling you -- you requested a User type, but you didn't tell GraphQL at least one field to get back from that type.
To fix it, just change your request to include name like this:
{
me {
name
}
}
... or age. Or both. You cannot, however, request a specific type and expect GraphQL to provide all the fields for it -- you will always have to provide a selection (one or more) of fields for that type.

History process instance query filter on boolean variable

ENV: camunda 7.4 with tomcat8, jdk1.8.0_40 and h2 in-memory database.
API: REST -> /history/process-instance (GET)
Filter on boolean variable like below
oneFlag_eq_false
does not work. The API document for that endpoint says the value will be treated as String on server side. Anyone knows how to apply this on a boolean variable?
oneFlag_eq_0
does not work either.
oneFlag_neq_1 or oneFlag_neq_true
doesn't do the filter at all.
The POST /history/process-instance query allows to query with boolean values.
Example reqeuest body:
{
"variables": [
{
"name": "oneFlag",
"operator": "eq",
"value": false
}]
}