How do I pass an ngrx actionCreator as a function parameter? - ngrx-store

How can I type the action parameter of the loader function so that it does not use TypedAction, which is not exported by ngrx?
I want to ensure type safety as the current example does.
const loadItemsAction = createAction(
'load items',
props<{ items: Item[] }>()
);
function loader(action: ActionCreator<
string,
(props: {
items: Item[];
}) => TypedAction<string> & { items: Item[] } // TypedAction is not exported from ngrx!
>){
return action({ items: [{name: 'item1'}, { name: 'item2'}]});
}
I can effectively destructure TypedAction as follows, but it loses the indication that an Action is being returned. Is there a better way?
function loader(action: ActionCreator<
string,
(props: {
items: Item[];
}) => { readonly type: string, items: Item[] }
>){
return action({ items: [{name: 'item1'}, { name: 'item2'}]});
}

I couldn't get anywhere with the existing ngrx types so added my own interface which identifies the object as an ActionCreator and allows easy specification of the type of those objects:
export interface ActionCreatorWithProps<TPropArgs extends object = {}>
extends ActionCreator<
string,
(props: TPropArgs) => TPropArgs & { readonly type: string }
> {}
used in original example:
function loader(action: ActionCreatorWithProps<{ items: Item[] }>) {
return action({ items: [{name: 'item1'}, { name: 'item2'}]});
}

Related

How to do a nested mutation resolver with nexus-prisma

I have the following datamodel:
type Job {
// ...
example: String
selections: [Selection!]
// ...
}
type Selection {
...
question: String
...
}
I define my object type so:
export const Job = prismaObjectType({
name: 'Job',
definition(t) {
t.prismaFields([
// ...
'example',
{
name: 'selections',
},
// ...
])
},
})
I do my resolver this way:
t.field('createJob', {
type: 'Job',
args: {
// ...
example: stringArg(),
selections: stringArg(),
// ...
},
resolve: (parent, {
example,
selections
}, ctx) => {
// The resolver where I do a ctx.prisma.createJob and connect/create with example
},
})
So now in the resolver I can receive the selections as json string and then parse it and connect/create with the job.
The mutation would look like this:
mutation {
createJob(
example: "bla"
selections: "ESCAPED JSON HERE"
){
id
}
}
I was wondering if there's anything more elegant where I could do something like:
mutation {
createJob(
example: "bla"
selections: {
question: "bla"
}
){
id
}
}
or
mutation {
createJob(
example: "bla"
selections(data: {
// ...
})
){
id
}
}
I've noticed that with nexus-prisma you can do stringArg({list: true}) but you can't really do objects.
My main question is what is the most elegant way to do either nested mutation or connect all in one.
You can use an inputObjectType as shown in the docs:
export const SomeFieldInput = inputObjectType({
name: "SomeFieldInput",
definition(t) {
t.string("name", { required: true });
t.int("priority");
},
});
Make sure to include the type as part of the types you pass to makeSchema. You can then use it to define an argument, like
args: {
input: arg({
type: "SomeFieldInput", // name should match the name you provided
}),
}
Now, the argument value will be available to your resolver as a regular JavaScript object, not a String. If you need a list of input objects, or want to make the argument required, you do so using the same options you would provide with when using a scalar -- list, nullable, description, etc.
Here's a complete example:
const Query = queryType({
definition(t) {
t.field('someField', {
type: 'String',
nullable: true,
args: {
input: arg({
type: "SomeFieldInput", // name should match the name you provided
}),
},
resolve: (parent, { input }) => {
return `You entered: ${input && input.name}`
},
})
},
})
const SomeFieldInput = inputObjectType({
name: "SomeFieldInput",
definition(t) {
t.string("name", { required: true });
},
});
const schema = makeSchema({
types: {Query, SomeFieldInput},
outputs: {
...
},
});
Then query it like:
query {
someField(
input: {
name: "Foo"
}
)
}
Or using variables:
query($input: SomeFieldInput) {
someField(input: $input)
}

Dynamic GraphQLObjectType

I'm trying to create a dynamic GraphQLObjectType with graphQl, something like this:
export const Project = (data) => {
return new GraphQLObjectType({
name: 'Project',
fields: () => ({
id: {
type: GraphQLString
},
type: {
type: GraphQLString
},
author: {
type: User,
resolve: (root, args, req) => {
...
}
}
})
})
};
I call this model on my query in this way:
getProjectById: {
type: Project(structure),
args: {
id: { type: GraphQLString }
},
resolve(source, args, req) {
const projectService = new ProjectService(req);
return projectService.getProjectById(args.id)
}
}
the problem is that doing this I get this error:
Schema must contain unique named types but contains multiple types
named "Project"
where is the error? do you have some advice? many thanks
The call Project(structure) in turn calls new GraphQLObjectType({name: 'Project',...}) . If you invoke Project(structure) more than once, you try to declare multiple GraphQLObjectTypes with the same name (which makes no sense).
If you would create/declare GraphQLObjectType dynamically, you have to generate a unique name property. E.g. like this:
// assuming data.name is unique
export const Project = (data) => {
return new GraphQLObjectType({
name: `Project${data.name}`,
...
})
}

Graphql Typecheck on redux like events

I'm implementing a graphql server over some existing REST api using apollo-server.
There is and endpoint returning a list of redux-like events, where Hi have a type and a payload.
type is a string and payload is an object. e.g.
[
{
type:"joined"
from:"member1"
payload:{
received_events:"1518377870416"
invited_by:"member2"
}
},
{
type:"text"
from:"member1"
payload:{
test_string:"hello"
}
}
]
What I need to check is the following:
1) type is an enum joined|text
2) from is a String
3) if type == joined then the payload should contain received_events and invited_by, if type == text then payload should contain test_string
What is the best way to do it? I'm looking at the scalar , and Union, but I'm not sure what to do.
One way to solve this is to inject the type from your event type into the payload object. It can then be used inside the __resolveType resolver for your union. A simple example:
const typeDefs = `
type Query {
events: [Event]
}
type Event {
type: String
payload: Payload
}
union Payload = Text | Joined
type Text {
test_string: String
}
type Joined {
received_events: String
invited_by: String
}
`;
// Since your type names and the specified types are different, we
// need to map them (resolveType needs to return the exact name)
const typesMap = {
text: 'Text',
joined: 'Joined'
}
const resolvers = {
Query: {
events: (root, args, context) => {
return [
{ type: 'text', payload: { test_string: 'Foo' } }
];
},
},
// We can use the resolver for the payload field to inject the type
// from the parent object into the payload object
Event: {
payload: (obj) => Object.assign({ type: obj.type }, obj.payload)
},
// The type can then be referenced inside resolveType
Payload: {
__resolveType: (obj) => typesMap[obj.type]
}
};

How to pass arguments to resolve function in a GraphQL Object?

I'm using GraphQL in Javascript, and I'd like to be able to pass arguments to a resolve() function in a GraphQLObjectType's field.
Here is the GraphQLObjectType declaratio:
export const ModelSchema = new GraphQLObjectType({
name: 'Model',
description: 'Model information',
fields: () => ({
tags: {
type: TagList,
description: 'Model\'s UUID',
async resolve(obj, args) {
console.log('args', args); // expecting to see an object
},
},
}),
});
And here is how I want to query in GraphQLI:
{
getModels(UUIDs:"0AAAA2EFF6677194ED227EE4AAAA8D4A") {
total
models {
tags (limit: 1) {
tags {
UUID
name
}
}
}
}
}
So I want to be able to send parameters (in this case limit) to tags so when the resolve() function is being invoked, I can use this parameter and limit the results, or do something else.
How can I do it?
Thanks
Ok, got it... Needed to add args like so:
export const ModelSchema = new GraphQLObjectType({
name: 'Model',
description: 'Model information',
args: {
limit: {
type: GraphQLInt,
},
},
fields: () => ({
tags: {
type: TagList,
description: 'Model\'s UUID',
async resolve(obj, args) {
console.log('args', args); // expecting to see an object
},
},
}),
});
And now it works.

How do i create a graphql schema for a self referencing data hierarchy?

This doesnt work because the type refers to its self in the routes field definition:
var routeType = new GraphQLObjectType({
name: 'MessageRoute',
fields: {
name: {
type: GraphQLString
},
routes: {
type: new GraphQLList(routeType),
resolve: (route) => {
return route.routes;
}
}
}
});
so how do I do it?
A GraphQL type can refer to itself (or refer to another type defined later in a file) by defining fields as a function that returns an object rather than an object. The function will be called after the page has been fully parsed.
For your example:
var routeType = new GraphQLObjectType({
name: 'MessageRoute',
fields: function () {
return {
name: {
type: GraphQLString
},
routes: {
type: new GraphQLList(routeType),
resolve: (route) => {
return route.routes;
}
}
};
}
});
Or, if you're using ES6, a nice shorthand for this using arrow functions:
var routeType = new GraphQLObjectType({
name: 'MessageRoute',
fields: () => ({
name: {
type: GraphQLString
},
routes: {
type: new GraphQLList(routeType),
resolve: (route) => {
return route.routes;
}
}
})
});
I'd like to point out that you can use a function for any property inside an object using Javascript getter.
So instead of wrapping the whole fields property within a function you can use a function just for the type property like this:
var routeType = new GraphQLObjectType({
name: 'MessageRoute',
fields: {
name: {
type: GraphQLString
},
routes: {
get type() {
return new GraphQLList(routeType)
},
resolve: (route) => {
return route.routes;
}
}
}
});

Resources