I'm building a GraphQL server from scratch with an API backend, replacing a REST API server. Currently some requests in the existing API server, mainly Create / Update requests that will be mutations in GraphQL, include a request id which is used by the client. This request id is metadata about the request itself not part of the domain resource.
My question is how to model / include request metadata in GraphQL queries and mutations? My only solution so far is to make a request Type, which other types could include as a field so the request data can be included in the mutation return type. However i do not like this approach as it mixes request data into my domain types and this very much feels against the ethos of GraphQL. Is there an accepted way to pass 'arbitrary' e.g. non-domain type data in the response of a query or mutation in GraphQL?
Example - what i would like to avoid:
type UserType {
id: ID
name: String
request: RequestType // mixing request data in with domain type of User
}
type RequestType {
id: ID
}
Update
For others interested in this problem, based on the responses here I have decided that the GraphQL extensions key is good option for adding arbitrary data to a GraphQL response without the data becoming part of your data Graph. In Express-GraphQL, docs on adding an extensions key to responses can be found here: https://github.com/graphql/express-graphql#providing-extensions. A good overview of extensions here: https://marmelab.com/blog/2017/09/06/dive-into-graphql-part-iii-building-a-graphql-server-with-nodejs.html#server-logging
That said, if the request metadata is conceptually part of the domain data then following Kashif's suggestion below of creating ResponseTypes that embed domain Types might be the correct approach.
Since GraphQL was designed to be protocol agnostic, you should use your chosen protocol's metadata pattern. If you're using HTTP, you should pass headers. If you're using WS/STOMP/MQTT/AMQP you can include metadata in each frame.
Alternatively GraphQL has an extensions protocol. You could add a request-id to your responses in the extensions object. Apollo uses extensions for caching, telemetry, etc.
We've been using GraphQL in production for about a year, we made the mistake of adding metadata into GraphQL and it gets hard to manage. Don't lose out on the benefits from the features of the protocol you're using.
This kind of meta elements are usually passed in HEADERS with each request. And then you can handle those in your GraphQL server as you like
For eg.
//Request Headers
...
X-Request-Id: <Your_Request_Id>
...
Updates
Headers work both ways so there shouldn't be any problems using them on client too. Pull them out of the response whenever you need.
However if you really want the requestId in your mutation response then that IS a part of your domain. And there is nothing wrong in have a custom response type, Many existing GraphQL apis do have a custom response type like LoginResponse, RegisterResponse which then wraps around your domain objects but also include extra stuff in meta fields
Related
We are developing a chatbot to handle internal and external processes for a local authority. We are trying to display contact information for a particular service from our api endpoint. The HTTP request is successful and delivers, in part, exactly what we want but there's still some unnecessary noise we can't exclude.
We specifically just want the text out of the response ("Response").
Logically, it was thought all we need to do is drill down into ${dialog.api_response.content.Response} but that fails the HTTP request and ${x.content} returns successful but includes Tags, response and the fields within 1.
Is there something simple we've missed using composer to access what we're after or do we need to change the way our endpoint is responding 2? Unfortunately the MS documentation for FrwrkComp is lacking to say the very least.
n.b. The response is currently set up as a (syntactically) SSML response, this is just a test case using an existing resource.
Response in the Emulator
Snippet from FwrkComp
Turns out it was the first thing I tried just syntactically correct. For the case of the code given it was as simple as:
${dialog.api_response.content[0].Response}
I am making an application that shows information about different users, which is taken from third party API. I save this information in my own format with multiple tables in PostgreSQL to keep track of any changes to the data and provide history of changes (third party API only provides current data).
I want to use GraphQL, specifically Postgraphile to simplify backend development. But there is one use case which I can't find a way to implement with Postgraphile. Here is what I want to implement:
User wants to see an updated information
GraphQL mutation query is sent to the server, something like this:
mutation UpdateUserData($userid: Int) {
updateUser(id: $userid) {
field1,
field2,
relatedObjects {
field3,
filed3
}
}
}
Server makes an API request to third party server, processes data, makes some calculations and updates the database
Requested fields are returned to client
I know that this type of custom mutations can be implemented with database functions like PL/pgSQL or PLV8, but they can't make http requests and I already have most of the logic for data processing in Typescript, so would like to use it.
Is there a way to create a custom mutation that will call JavaScript function which has access to Node.js modules, interfaces and classes that I already created?
One solution that I think will work:
Call REST endpoint on my server, like /update_user?id=$userid
User data is loaded from third party API and database is updated
Client receives response, like Update successful
Normal GraphQL query is called to get the data
Is there a better way of satisfying this use case?
This part is a bit hidden in the documentation, but the easiest way to add mutations written in JavaScript is the makeExtendSchemaPlugin.
Here you can define type definitions in SDL and implement resolvers in JS.
http://localhost:4200/product-list?gender=2&category=4
http://localhost:4200/product-list?gender=2
http://localhost:4200/product-list?category=4
i want to use one backend controller for the endpoints.
can i do that? i tried but with no success. (using angular)
What exactly did you try with angular? The backend controller? Angular is a frontend framework.
However, you could indeed have one single controller (e.g. ProductController) for your endpoints. I would suggest implementing a "getProducts" method that can be filterable. That way you can use single endpoint and provide optional filter options as needed.
I may not understand your question entirely, so please stat otherwise. But, Yes. You have one endpoint stated above, with different query params. Depending on the different query params (their value or even if they are present), your singular endpoint can do something different.
So your endpoint is
http://localhost:4200/product-list
This endpoint (which there is only one), can have as many query params as you like. As per the response above, Angular does not handle this, this would be some backend functionality for your end point, to read the request, and based on what query params are 'found' then trigger a different response.
I'm using swagger to define my API and API-gateway to host this API. I found the following lib (see here) to import my API definition to AWS and automatically create the API (models, end-points, etc). It's cool. But, it's not able to validate Requests based on models (defined in Swagger definition). It means that you can send a JSON payload without the required fields.
I don't want to write a node.JS code to check the format because it will not be much easy for schema updates. I'm wondering if it's possible to check if a JSON payload is compatible with a specific type of object defined in swagger (objects defined in the "definitions" section).
If it's possible, it will allow me to only update my swagger definition.
Thanks,
Romain.
We are tracking this feature request on our backlog. For clarity, this would be implemented in the API Gateway service, not the Swagger importer. In the meantime you will need to implement validation logic yourself. See here for libraries which may help: http://json-schema.org/implementations.html
So we're developing a web application in Symfony2 (brief editorial: Symfony2 is freaking awesome) along the lines of an SOA. All data is farted back and forth between our jQuery powered frontend and the Symfony2 backend formatted a la JSON, and therein lies the rub.
Symfony2 provides for a robust security system, but it seems to hinge on the "Security Layer" intercepting form submissions and using the form-encoded POST data to process an authentication attempt. This is problematic for our application because we use JSON exclusively. From where I'm standing, using JSON for every single request and response except authentication is... the sheet of the bool, frankly. Bad smell, bad juju, whatever you call it.
Now, Symfony2 allows for the creation of event listeners that hook into a series of events related to the lifecycle of a request and the consumate response. We use one of these hooks to decode the JSON that comes in with every POST request so that the relevant controller only ever has to worry about working directly with a php array and not do any decoding or de-serializing or whatever.
So the crux of our issue is that the "Security Layer" expects that form-encoded POST data that it gets from a form submission (generally on a page that the backend served in the first place). We're set up to feed it a PHP array created from JSONified data. So what do? Should we:
Create a custom authentication service that is built to deal with an array made from le JSON?
Tweak our request hook to check the target uri of each request and subsequently massage the request's JSON into the form-encoded string the "Security Layer" expects?
Tweak the "Security Layer" so that it can work the the JSON turned php array?
It's fairly simple to create your own authentication provider. You can follow this cookbook article and modify it slightly to handle your JSON request instead of the WSSE used in that example.