graphql / graphql-relay-js Goto Github PK
View Code? Open in Web Editor NEWA library to help construct a graphql-js server supporting react-relay.
License: MIT License
A library to help construct a graphql-js server supporting react-relay.
License: MIT License
I've followed the setVariables example for creating a pagination in nodes, however is not working with the current changes: syrusakbary/relay-starter-kit@75703bb
Here is the error I'm getting in the browsers console:
Uncaught Error: Invariant Violation: RelayQuery: Field `widgets` does not have an argument `after`.
Related issue in Relay Starter Kit: facebookarchive/relay-starter-kit#3
Would it be possible to define connectionArgs
per https://github.com/graphql/graphql-relay-js/blob/v0.3.2/src/connection/connection.js#L29 as e.g. the union of:
export var connectionWithFirstArgs: GraphQLFieldConfigArgumentMap = {
after: {
type: GraphQLString
},
first: {
type: GraphQLInt
},
};
export var connectionWithLastArgs: GraphQLFieldConfigArgumentMap = {
before: {
type: GraphQLString
},
last: {
type: GraphQLInt
},
};
export var connectionArgs: GraphQLFieldConfigArgumentMap = {
...connectionWithFirstArgs,
...connectionWithLastArgs,
};
Per the spec: http://facebook.github.io/relay/graphql/connections.htm#sec-Arguments, connections don't have to implement both sets of arguments, so it'd be nice to have a helper directly from graphql-relay-js
for defining just one pagination direction.
When resolving a connectionType, it'd be nice if you could pass optional information back to custom connectionFields
defined within customDefinitions
.
I'm working on a search connection type atm and I want to be able to set as connection fields total
and took
(the search time). Those would necessarily only be known within the searchConnection
resolve
function.
If given some guidance on a solution, I can put together a PR tomorrow.
Hi,
I was wondering what is the use case for returning the cursor along with every node in a connection. Given that pageInfo
has the start and end cursor, that would suffice for pagination purposes as far as I understand.
The reason I'm asking is because there is too much indirection in the schema (and therefore, in the queries) when retrieving fields from a connection:
{
articles {
pageInfo { ... }
edges {
node {
title
}
}
}
}
Ideally this could be:
{
articles {
pageInfo { ... }
nodes {
title
}
}
}
which removes one level of indirection.
Thanks for your time.
Hey guys,
I was wondering if anyone could shed some light on how edge cursors work, and how they are generated.
I am trying to leverage the paging functionality provided by "connections", but I am not sure whether my cursors are being generated properly. At first I thought a cursor was the global id of the edge object, which I could have converted in its original id using fromGlobalId(args.after) but it seems that isn't the case.
I need to get at the original id, so that i can structure my SQL query to only retrieve results that occur after my original id. My sql database has no knowledge of the cursors generated in graphql, so I'm at a loss on how to make that connection.
Thanks in advance!
Our web applications have been using GraphQL for a while now and those teams built out the GraphQL service that wraps our backends, however, they are not yet using React or Relay. We’re now using it in our iOS app, but through React Native and Relay. For the first version of out iOS app, that’s currently in the app store, we did not yet needed specific Relay support in our GraphQL service.
Now I’m trying to add full Relay support to the GraphQL service, but am struggling a bit with the required schema to support Global Object Identification. The issue is that our schema already has an id
field, that maps to an ID on the backend. The various web clients are already using this assumption all over the place, but what’s worse is that the iOS app also assumes this in 1 place, which means that if I ever change the semantics of the id
field it basically breaks for all people that have not yet upgraded the app.
In our case, the place where the iOS app uses it isn’t 100% critical (it’s related to some analytics code) and the web clients are all in our control, so those could all be patched and upgraded. However, it seems to me that id
is such a common and ambiguous name, it might make more sense to be able to change the field that Relay depends on instead.
My thought is to update Relay to either:
id
relayID
over id
, if that field exists in the schemaWould either be acceptable?
I saw some imported type from graphql
.
like connection.js#L20 :
import type {
GraphQLFieldConfigArgumentMap,
GraphQLFieldConfigMap
} from 'graphql';
But i can neither find .js.flow
output in graphql
's package, nor a ".flowconfig"-style lib, and there is also no graphql
in flow's lib too.
So where do these types come from? Did the type check pass ,just because if flow can not found a flowed package, it will set those type to any type
?
Or is there some new way for outputting flowTypes?
:D
Sharing nodeInterface
across the different GraphQL types in a splitted schema can lead to weird circular dependency issues.
Considering the fact that the different field
types (in mutationWithClientMutationId
or GraphQLObjectType
) are evaluated lazily when defined as thunks, could we make it so the interfaces
field is also evaluated lazily when defined as a thunk ?
Currently, it's evaluated immediately no matter how it is defined, which is not the expected behavior.
Hey, a pretty common situation is that getting the list of connected IDs is cheap (i.e. the list of the IDs of your friends), but resolving each ID is expensive.
Specifically for this kind of use case: clayallsopp/graphqlhub@07fec4c#diff-6cd9d3d473d2f9b30b961ebfb3d3f048R50 - I have the entire list of IDs in memory, so if the query specifies first: 5
or provides a cursor, it would be great to reduce the set of IDs we're resolving before we resolve them. the current connectionFromPromisedArray
doesnt help in this regard, as you have to resolve the entire list and then it trims after-the-fact
I think something like this API would be very helpful in general - I think it's possible? (assuming you have the entire list of IDs in memory and cursors are pure functions of offset and IDs)
let connectionIds = item.kids;
return promisedConnectionFromIdsArray(connectionIds, args, (trimmedIds) => {
return Promise.all(trimmedIds.map(getItem));
});
I guess this is as much a question to the graphql+relay spec, but I'm confused with regards to the lack of 'position' as an argument. I know that the reason it uses complicated pagination is because of unbounded and order-independent results, but I have a use case I'm not sure how to go about doing in graphql+relay (though it's quite easy so I'm sure I just missed something).
How do I, say, get the 5th item (and only 5th item), if my list is ordered (by, say, an orderBy argument).
From facebook/relay/issues/755:
Is there a reason why you user ascii as encoding and decoding base64 ids ? I got bitten by this as i had a swedish character in an id and was incorrectly encoded. Changed ascii to utf-8 in graphql-relay\lib\utils\base64.js and it worked.
cc @quazzie
Looks like you've already updated package.json to have a peer dependency on the latest version of [email protected].
Can you please do a 0.3.7 release so consumers can run the latest version of the graphql lib while satisfying your peer-dependency?
The need come from displaying the user a list of nodes that she should be able to sort/filter.
I'd like to get a better grasp on the philosophy behind Connection
s and how would one go in building a backend to support the {before,after,first,last}
pagination along with ordering/filtering.
From graphql/graphql-spec#4 I understand that GraphQL per se doesn't really care on how you handle pagination/filtering/ordering. Hence I'm writing here even if I realize that this is not strictly in the Relay scope.
My general question is: how do you go in handling filtering and especially sorting the Relay way (if at all) and why?
Lets say we could query with a Relay server like:
currentUser {
friends(first:10, after:"opaque-cursor", order:AGE) {
edges: [ ... ]
}
}
I guess my questions are:
AGE
would be an Enum value) or would be querying for a different firendsOrderedByAge
connection be more aligned with Relay philosophy? Or what else?friends(order:[AGE,POSTCOUNT], ...)
Connection
model, how would you go in building a backend API that does?{after_age, after_id}
from your opaque cursor in the after
parameter; would a pseudo-SQL like this make sense? SELECT * FROM a_table WHERE (age, id) > (after_age, after_id) ORDER BY age ASC, id ASC LIMIT first
Thank you all!
I may be doing something wrong, however as soon as I import 'graphql-relay' package I get the following error.
Thank you.
I can get nodeInterface
and nodeField
from nodeDefinitions
like this
let {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
return null;
}
);
And the first questions is, that If I need just nodeInterface and I will not query on nodeFiled, can I return always null? Or I will need it somewhere?
If I did not pass typeResolve as second parameter for nodeDefentions, in the execution moment for each runtime GraphQLObjectType will be called isTypeOf().
So why should I check the type in isTypeOf
method?
Next example working fine.
let {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
// If I need just nodeInterface and I will not query on nodeFiled, can I return always null? Or I will need this for nodeConnections?
return null;
}
);
let userType = new GraphQLObjectType({
name: 'User',
description: 'User in application',
isTypeOf: (value, info) => {
// Can I always return true for each GraphQLObjectType which uses nodeInterface without typeResolver function?
return true
},
fields: () => ({
id: globalIdField('User'),
name: {
type: GraphQLString
}
}),
interfaces: [nodeInterface]
});
let oneMoreType = new GraphQLObjectType({
name: 'OneMoreType',
description: 'Some another type in application',
isTypeOf: (value, info) => {
// Can I always return true for each GraphQLObjectType which uses nodeInterface without typeResolver function?
return true;
},
fields: () => ({
id: globalIdField('User'),
field: {
type: GraphQLString
}
}),
interfaces: [nodeInterface]
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Root',
description: 'Root field in application',
fields: () => ({
user: {
type: userType,
resolve() {
return {
id: '1',
name: 'First'
}
}
},
oneMoreType: {
type: oneMoreType,
resolve() {
return {
id: '3',
field: 'Some field'
}
}
}
})
})
});
export default schema;
How can I get in method mutateAndGetPayload rootValue ?
graphQLServer.use('/', graphQLHTTP((req) => (
{ schema: Schema, rootValue: { req }, pretty: true }
)));
Mutations are just fields, so they can be documented with a 'description' as other fields.
I think mutationWithClientId should support a 'description' arg and pass it on to the created field.
[email protected] requires a peer of graphql@~0.4.2 but the latest version is [email protected]
Updating the package.json should do the trick
We could check rootValue or context to determine request's role permission for graphql request.
How do we check permission for nodeDefination (request thru 'node' ) ?
You're gonna hate this one 😸
I know, I know, naming things, etc. But I'd be remiss if I didn't at least make some suggestions.
So, I think Connections could be called PagedAssociations – or something along those lines. I feel like Connection has a very network/database connotation to it, and it makes it quite confusing for newcomers approaching a library that does actually deal with both network connections and probably database connections too – especially because they're being defined in the schema alongside database resolving functions.
I also think edges
and node
would perhaps be a little more newbie friendly if they were just items
and item
.
Based on the ConnectionConfig
, node type is required to be a GraphQLObjectType
:
https://github.com/graphql/graphql-relay-js/blob/master/src/connection/connection.js#L62
But according to the spec:
An “Edge Type” must contain a field called node. This field must return either a Scalar, Enum, Object, Interface, Union, or a Non‐Null wrapper around one of those types. Notably, this field cannot return a list.
https://facebook.github.io/relay/graphql/connections.htm#sec-Node
Is it a bug, or did I misunderstood the spec?
Since I was having trouble getting false negatives from hasPreviousPage
- I wanted to see how you were testing it, but it appears it is not being considered in this test seen here:
My resolve function retrieves data from a database using the standard first
argument as a limit.
resolve(_, args) => {
criteria = makeCriteriaFromArguments(args);
criteria.limit = args.first;
const result = stuffRepository.findAll(criteria);
return connectionFromPromisedArray(result, args);
};
In this case, when connectionFromPromisedArray
thunks over to connectionFromArraySlice
, the end offset and the upperbound will be equal (https://github.com/graphql/graphql-relay-js/blob/master/src/connection/arrayconnection.js#L121). The resultant PageInfo, emitted by connectionFromPromisedArray
would have
{ hasNextPage: false, ... }
For this use case, I would expect this:
hasNextPage: first != null ? endOffset <= upperBound : false,
instead of
hasNextPage: first != null ? endOffset < upperBound : false,
I could resolve this by overfetching my collection by one, but that would be really hacky.
In connection/arrayconnection.js
, It seems all the function is tend to work with array
.
For example offsetToCursor
is the only way to generate Cursor. Does this mean its a design pattern i must follow, or imply that i should generate Cursor by myself when using something other than array
.If im planning to use Mongodb,should i make the database interface like an static array ?
BTW:
As a newbie to web develop, im a bit confused how to implement a qualified relay server.
Are there some guide for design a graphql-relay server, should i follow all the way in graphql-relay-js
, which Database Facebook used with relay-server ? mysql or ?
Im not sure ask this here is appropriate or not,but the topic for graphql-relay-js is rarely on the web.
Thanks a lot, forgive my impolite.
var PREFIX = 'arrayconnection:';
/**
* Creates the cursor string from an offset.
*/
export function offsetToCursor(offset: number): ConnectionCursor {
return base64(PREFIX + offset);
}
I'm fetching data from MySQL database, but I'm unable to resolve types of returned data by instanceof
operator or by it's content (they are just pure JS objects, they don't have any specific class). My nodeDefinitions
looks something like that:
let {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
let {type, id} = fromGlobalId(globalId)
return db.findOne(type, id)
},
(obj) => {
// can't resolve type of obj
}
)
What is the prefered way to solve this problem? So far I invented just this, but it's ugly because it change returned object:
let {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
let {type, id} = fromGlobalId(globalId)
return Object.assign({_type: type}, db.findOne(type, id))
},
(obj) => {
if(obj._type === 'User') {
return userType
} else if(obj._type === 'Article') {
return articleType
}
// ... and so on
}
)
Can you think of any better solution? Thank you.
Hi everyone,
I think that mutationWithClientMutationId should accepts also a thunk as outputFilelds.
I need to create payloads with type which are not ready at definition time.
Array.indexOf is not very flexible and if I am retrieving item and array separately from a db, I have to manually find an element in an array to pass it into cursorForObjectInConnection.
Wouldn't it be more appropriate to use findIndex or isEqual from Lo-Dash?
Looking at this test case:
graphql-relay-js/src/connection/__tests__/arrayconnection.js
Lines 172 to 196 in 7422cfe
describe('pagination', () => {
it('respects first and after', () => {
var c = connectionFromArray(
letters,
{first: 2, after: 'YXJyYXljb25uZWN0aW9uOjE='}
);
return expect(c).to.deep.equal({
edges: [
{
node: 'C',
cursor: 'YXJyYXljb25uZWN0aW9uOjI=',
},
{
node: 'D',
cursor: 'YXJyYXljb25uZWN0aW9uOjM=',
},
],
pageInfo: {
startCursor: 'YXJyYXljb25uZWN0aW9uOjI=',
endCursor: 'YXJyYXljb25uZWN0aW9uOjM=',
hasPreviousPage: false,
hasNextPage: true,
}
});
});
Because there is also an A
and a B
- shouldn't hasPreviousPage
be true
? If not, then how do we know there is a previous page available??
Hi,
It looks like there is a spec violation in connectionFromArraySlice implementation. Specifically, spec of the Relay pagination algorithm (4.3 / ApplyCursorsToEdges / Step 2.2) indicates that elements should be removed from the slice only if afterEdge
exists (if afterEdge
does not exist, allEdges
are returned) [1]. Here is a counter example:
connectionFromArraySlice(
[1, 2, 3, 4, 5, 6],
{ first: 6, after: 'YXJyYXljb25uZWN0aW9uOjEw' },
{ sliceStart: 0, arrayLength: 6 } )
This returns empty edges array. Argument after
in this case is base64 encoded "arrayconnection:10"
string. Clearly, edge corresponding to that cursor doesn't exist, so connectionFromArraySlice
should return all six nodes in the edges
array as per the spec.
There is no symmetrical issue with last / before combination, because endOffset is set to min of beforeOffset and array length.
Just wanted to confirm that my read of the spec makes sense to you all before sending a pull request with the fix.
Thanks,
Nemanja
[1] Spec: https://facebook.github.io/relay/graphql/connections.htm#ApplyCursorsToEdges()
I used a flow-type output version graphql-relay-js
in my project. And at this time the graphql/graphql-relay-js
's master branch have some unpassed flow-type check.
The most critical part for type check is something like resolve
.( Cause raw graphql allow source to mixed type.and args to a simple map, Where Relay narrowed source to object ,and use a single input
key for args. And ,the most of unpassed type checks in graphql-relay
with graphql
are caused by this).
GraphQLFieldResolveFn
(graphql#L469)
type GraphQLFieldResolveFn = (
source: mixed,
args: {[argName: string]: mixed},
context: mixed,
info: GraphQLResolveInfo
) => mixed
which in Relay should be something like :
ype RelayFieldResolveFn = (
source: Object,
args: {'input':Object},
context: mixed,
info: GraphQLResolveInfo
) => mixed
Is there some elegant way to type check graphql-relay
with graphql
( and narrowed resolveFn rightly)?
i got two idea,but
seems modified GraphQLFieldResolveFn
to some template type will lead to lots of changes in graphql.
And adding some rumtime check in graphql-relay
looks like inefficiently.
Is there some good way to do this?
In the case that you have a custom cursor defined via resolveCursor
, I would think the generated pageInfo should also be run through it, else the pageInfo.startCursor
/ pageInfo.endCursor
will be the default cursors, not the custom ones.
I actually ended up getting around this issue by generating my own pageInfo from scratch, since in my case connectionFromArray wasn't doing anything special for me (I'm pulling data from a DB, and don't have access to allItems), but if this was an oversight but intended behavior, I thought I'd bring it to light.
export const {connectionType: messageConnection, edgeType: MessageEdge} = connectionDefinitions({
name: 'Message',
nodeType: MessageType,
resolveCursor: ({cursor, node}) => {
return Base64.encode('createdAt:'+moment(node.get('createdAt')).unix());
},
});
export const PostType = new GraphQLObjectType({
name: 'Post',
description: 'Post',
fields: () => ({
id: globalIdField('Post'),
messages: {
type: messageConnection,
args: connectionArgs,
resolve: async (post, args) => {
let messages = await getMessages(post.id, args)
let connection = connectionFromArray(messages, args);
// connection.edges is still using the default cursors, because resolveCursor will only get called after this resolve returns
// likewise, connection.pageInfo.startCursor and connection.pageInfo.endCursor are using the default cursors, but they won't be re-resolved using my custom resolveCursor()
return connection;
},
},
}),
interfaces: [nodeInterface],
});
connectionDefinitions function’s response type is GraphQLObjectType, which is wrong.
GraphQLConnectionDefinitionsType would describe response type right.
Hi guys!
I noticed that there's been a ton of commits since Nov 2 (the last release)...a lot of them are super useful!
Could you release a new version to npm? Currently I'm having to install this from Sinopia...but I'd like to go back to npm.
Thanks!
-Adam
AFAICT graphql-js
allows you to leave out resolveType
here:
In which case it will fall back to using getTypeOf
/isTypeOf
The problem is, nodeDefinitions
doesn't seem to allow you to leave out the typeResolver
function:
graphql-relay-js/src/node/node.js
Line 55 in 3dd1fc3
Making this optional would certainly reduce a lot of boilerplate code
Hello guys!
It seems to me than to pass a valid slice to connectionFromArraySlice you basically need to know the start offset and end offset to make the slice bigger. Do you agree?
If yes, I'm happy to extract this piece into its own function and add tests :)
Cheers
The current implementation of nodeDefinitions
seems to be a pretty thin wrapper around the object resolution and abstract type resolution. This makes for an interface to nodeDefinitions
that feels fairly awkward.
For example, here: https://github.com/relayjs/relay-starter-kit/blob/9f30837849362b65181e6383cfd52ad402a9c00e/data/schema.js#L52 - it feels like it'd be much easier to do something like return {type: userType, value: getUser(id)}
instead of separately specifying the type resolver in a different function below. I realize I can get something like a more decoupled syntax if I use isTypeOf
, but it seems strange to have to have any sort of explicit type resolution logic if I know a priori the concrete type at the time that I resolve the node value.
Is there any chance of modifying the implementation or the spec in such a way that it would be possible to simultaneously resolve both the result and the concrete type for abstract types, or at least add some wrappers for doing so for Relay nodes?
Because of this issues, I like to implement some function like 'connectionFromArrayWithNodeId' or make 'connectionFromArray' receive 'arrayToEdges'.
Could I add this function to this repository?
I'm slowly learning my way around Relay, and have written a few mutations so far with great success. None of my prior mutations have modified an existing record, though, so I don't have an example yet where I specify the id of something I'd like to update.
The mutation I'm currently working on will do just that, finally. I'm wanting to update an existing record. My mutation expects the id of the record as an argument. Coming from Relay, this is a globally unique id created with toGlobalId
, though. So, within mutateAndGetPayload
I've got access to that global id, which I figure I need to use fromGlobalId
to get my local SQL primary key value.
Is this the typical use case? I need to code in a type check for this id to make sure it's of the right type, etc? Or is there a way to say that the id parameter needs to be the same type as the id on my other type, and it'll do that fromGlobalId
for me?
Here's my mutation ...
import {mutationWithClientMutationId} from "graphql-relay";
import {GraphQLID, GraphQLNonNull} from "graphql";
import AccountType from "../type/AccountType";
import TeamInviteType from "../type/TeamInviteType";
import * as AccountSql from "../../data/query/sql/account";
import * as TeamInviteSql from "../../data/query/sql/team-invite";
export default mutationWithClientMutationId({
name: "AcceptTeamInvite",
inputFields: {
id: {
type: new GraphQLNonNull(GraphQLID),
description: "The id of the team invite."
}
},
outputFields: {
invite: {
type: new GraphQLNonNull(TeamInviteType),
resolve: source => source
},
account: {
type: new GraphQLNonNull(AccountType),
resolve: source => AccountSql.getById(source.account)
}
},
mutateAndGetPayload: async (source, info) => {
const {user} = info.rootValue;
if (!user) {
return Error("not allowed");
}
const invite = await TeamInviteSql.getById(source.id);
if (user.id !== invite.account) {
return Error("not allowed");
}
return TeamInviteSql.accept(invite.id);
}
});
Currently typeResolver
does not accept context
.
It would be useful to expose it.
I wanted to submit a PR and tried the following patch but graphql
always passes undefined
.
diff --git a/src/node/node.js b/src/node/node.js
index 30320ab..2c8d7bd 100644
--- a/src/node/node.js
+++ b/src/node/node.js
@@ -16,8 +16,8 @@ import {
import type {
GraphQLFieldConfig,
- GraphQLObjectType,
- GraphQLResolveInfo
+ GraphQLResolveInfo,
+ GraphQLTypeResolveFn
} from 'graphql';
import {
@@ -30,9 +30,6 @@ type GraphQLNodeDefinitions = {
nodeField: GraphQLFieldConfig
}
-type typeResolverFn = (object: any) => ?GraphQLObjectType |
- (object: any) => ?Promise<GraphQLObjectType>;
-
/**
* Given a function to map from an ID to an underlying object, and a function
* to map from an underlying object to the concrete GraphQLObjectType it
@@ -45,7 +42,7 @@ type typeResolverFn = (object: any) => ?GraphQLObjectType |
*/
export function nodeDefinitions(
idFetcher: ((id: string, context: any, info: GraphQLResolveInfo) => any),
- typeResolver?: ?typeResolverFn
+ typeResolver?: ?GraphQLTypeResolveFn
): GraphQLNodeDefinitions {
var nodeInterface = new GraphQLInterfaceType({
name: 'Node',
I'm trying to implement a login mutation, which would mutate the user's session. I used the example shown on [express-graphql|https://github.com/graphql/express-graphql#advanced-options] to add the session to the rootValue, but now I can't access that session in mutateAndGetPayload
. I recommend adding rootValue to the mutateAndGetPayload
call: necessary changes [here|https://github.com/graphql/graphql-relay-js/blob/master/src/mutation/mutation.js#L85].
I just noticed while preparing the v0.3.3 release that we don't have an actual changelog, so I had to link people to: v0.3.2...v0.3.3
Let's add one, so we can have something more human-readable, albeit at a slight maintenance cost.
I am trying to define a new mutation with a description:
const createNode = mutationWithClientMutationId({
name: 'createNode',
description: 'Creates a new node in the system',
inputFields,
outputFields
});
As I can see in the source code, it is not possible to provide a description field to the mutation.
Is this behavior correct?
see: https://github.com/graphql/graphql-relay-js#using-relay-library-for-graphqljs
The install line should more helpfully read:
npm install graphql graphql-relay
since the graphql
module is a peer dependency. npm3 no longer installs peerDependencies by default
I've only had about 10 minutes to look into this before I have to jet, but I wanted to create an issue so that I could check back when I return and see if anybody has some insight here.
I've got a few database query functions that return promises. On failure, those functions reject the promise.
In my mutationWithClientMutationId
code, I want to use these promise functions and basically never return an actual failure HTTP response, but instead I'd like to respond with an error message as part of the response payload. I'll deal with true server side failures later, but I'm trying to get some useful user error messages back to the React side of things.
I've got some code though that uses the promises in an attempt to respond with a payload that includes the error messages, but it seems to instead respond with the actual HTTP error response, instead of a 200 response but with my output fields. Here's my code ...
import {mutationWithClientMutationId} from "graphql-relay";
import {GraphQLString, GraphQLBoolean, GraphQLNonNull} from "graphql";
import {Sql} from "../../data";
function fetchToken({username, password}) {
return Sql.Account.getWebToken(username, password)
.then(token => ({ token }))
.catch(error => ({ error }));
};
function createAccount({username, password, summoner}) {
return Sql.Account.insert(username, password, summoner)
.then(fetchToken)
.catch(error => ({ error }));
};
export default mutationWithClientMutationId({
name: "CreateAccount",
inputFields: {
username: {
type: new GraphQLNonNull(GraphQLString)
},
password: {
type: new GraphQLNonNull(GraphQLString)
},
summoner: {
type: new GraphQLNonNull(GraphQLString)
}
},
outputFields: {
token: {
type: GraphQLString,
resolve: source => source.token
},
userError: {
type: GraphQLString,
resolve: source => source.error
},
didSucceed: {
type: new GraphQLNonNull(GraphQLBoolean),
resolve: source => source.error === undefined
}
},
mutateAndGetPayload: createAccount
});
Am I using these promises incorrectly? The Sql
functions return promises. The resolve
functions appear to be receiving the proper output from the createAccount
or fetchToken
functions (an error object). GraphQL is responding with an HTTP error though, as opposed to the desired successful response.
Does relay support pagination when the size of the collection is unknown? ie sometimes it is expensive to know the exact size of a collection or just needed from a product perspective. Like search results.
While I understand you need the exact size to support the 'last' argument if 'before' or 'first', what's the most relay-compliant way to support those cases? Maybe the answer is to not worry about it (pick an arbitrary long size) and make sure relay never emits a 'last' query on that connection. Any thoughts?
On a related note, any best practice on how to handle a non-static collection? like a facebook feed or an online chat list.
Thanks!
(I guess my questions are about about the spec, but this repo seems also appropriate.)
https://github.com/graphql/graphql-relay-js/blob/master/src/mutation/mutation.js#L83
I pass user object to rootValue, how can i get user object in mutations ?
If my connection args don't have the 'last' parameter, then hasPreviousPage will always be false. Same problem if you are paging backwards and don't have the 'first' parameter in your connection args: hasNextPage will always be false.
But I can't have a 'last' parameter if I am paging forward using 'first' and 'after'. And I can't have a 'first' parameter if I am paging backwards using 'last' and 'before'. Babel-relay-plugin will throw an error on transpile.
So if I am paging forwards, I will always be told I have no previous pages, even when I do. And if I am paging backwards I will always be told I have no next pages, even when I do.
This has gotta be a bug. It kinda ruins bi-directional paging.
Can't we just make paging easier and let us pass first and last and before and after all as connection args (some of them as null depending on which way you are paging) without babel-relay-plugin blowing up?
I was recently building a toy project with Relay and GraphQL where I needed to encode extra information in my global ID. To do this, I decided to add an idFetcher
argument to my globalIdField
call that returned a string that was delimited by a :
. However, this did not work because of how globalIds are deconstructed in fromGlobalId
, so instead of getting my encoded ID (e.g. 123:test
) I just got the ID part (e.g. 123
).
To prevent this confusion, it might be nice to either warn when this happens or use a different strategy in fromGlobalId
than split
. Here's a performant version that satisfies this use-case:
export function fromGlobalId(globalId: string): ResolvedGlobalId {
var unbasedGlobalId = unbase64(globalId);
var delimiterPos = unbasedGlobalId.indexOf(':');
return {
type: unbasedGlobalId.substring(0, delimiterPos),
id: unbasedGlobalId.substring(delimiterPos + 1)
};
}
Of course, you could also use a regex if you prefer (but I think I like the substring
solution):
export function fromGlobalId(globalId: string): ResolvedGlobalId {
var tokens = unbase64(globalId).match(/(.+?):(.+)/);
return {
type: tokens[1],
id: tokens[2]
};
}
Let me know what you think. I'll probably just submit a PR for this change for your convenience.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.