-
Notifications
You must be signed in to change notification settings - Fork 969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ts): Improves return types of QueryResolvers, MutationResolvers and <Model>Resolvers #6228
Conversation
☁️ Nx Cloud ReportCI is running/has finished running commands for commit 5d59f5f. As they complete they will appear below. Click to see the status, the terminal output, and the build insights. 📂 See all runs for this branch ✅ Successfully ran 14 targets
Sent with 💌 from NxCloud. |
✅ Deploy Preview for redwoodjs-docs ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
1c74d4b
to
9c9593f
Compare
…and <Model>Resolvers
3a6b563
to
878f942
Compare
) => TResult extends PromiseLike<infer TResultAwaited> | ||
? Promise<Partial<TResultAwaited>> | ||
: Promise<Partial<TResult>> | Partial<TResult>;` | ||
) => TResult | Promise<TResult>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This return Resolver's ReturnType to the default. I introduced this in strict-mode pr and have subsquently realised this is a terrible idea.
@@ -159,11 +185,11 @@ function getPluginConfig() { | |||
scalars: { | |||
// We need these, otherwise these scalars are mapped to any | |||
BigInt: 'number', | |||
DateTime: 'string', | |||
Date: 'string', | |||
DateTime: 'Date | string', // @Note: because DateTime fields can be valid Date-strings too |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Important change.
Redwood has built-in handling for Dates (because we https://www.graphql-scalars.dev/docs/scalars/date-time configured) - but our types were only allowing string. This was a misconfiguration, but not super visible, because we were mapping Prisma types anyway.
For DateTime
fields you can send back Date but also a valid date-string. Note that when you set DateTime
in your SDL, even if you format it yourself, it will convert it back to the RFC3339.
Note also that the code here only affects types we generate, I'm just highlighting what the behaviour actually is and updating the types to match it!
@@ -22,15 +22,30 @@ import { getTsConfigs } from '../project' | |||
|
|||
export const generateTypeDefGraphQLApi = async () => { | |||
const filename = path.join(getPaths().api.types, 'graphql.d.ts') | |||
const prismaModels = getPrismaModels() | |||
const prismaImports = Object.keys(prismaModels).map((key) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved importing Prisma types here, because we now use a "custom mapper" generic MergePrismaWithSdlTypes<>
in the mapper object.
72d4aa3
to
f787261
Compare
@@ -239,7 +239,7 @@ type Params = { | |||
* // key being used in dbAccessor in src/functions/auth.ts 👇 | |||
* const getCurrentUser = async (session: DbAuthSession<User['id']>) | |||
*/ | |||
export interface DbAuthSession<TIdType = unknown> { | |||
export interface DbAuthSession<TIdType = any> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Tobbe had to set this to any again, because it complains about currentUser function being passed into the graphql handler in strict mode otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next time I see this I'll get an itch to try to switch it to unknown
again... How did you notice it was complaining? If I can check on my own next time it doesn't have to bounce in a PR :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a tsc error in the projects graphql function.
It happens because findUnique (when trying to find the user) can return null I think
where: { id }, | ||
}) | ||
} | ||
``` | ||
|
||
### Relation resolvers in services |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting a little messy here with this.
This part of the doc is half ts strict mode - and half "dealing with quirks of gql+prisma"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not just this part, also the part above about removeNulls
@@ -172,7 +172,7 @@ storybookTest( | |||
await page.goto(STORYBOOK_URL) | |||
|
|||
// Click Redwood link in left nav | |||
await page.locator('css=[data-item-id=redwood--page]').click() | |||
await page.locator('id=redwood--page').click() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why I had to change this now 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm getting the same error in #6226, but it doesn't happen to any renovate PRs. Weird
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I take that back, was failing because I forgot to update a string here: 82ec512
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it works in CI, but fails locally without this change.
where: { id }, | ||
}) | ||
} | ||
``` | ||
|
||
### Relation resolvers in services |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not just this part, also the part above about removeNulls
packages/api/src/transforms.ts
Outdated
* @param obj - Object to remove nulls from | ||
* See {@link https://www.prisma.io/docs/concepts/components/prisma-client/null-and-undefined Prisma docs: null vs undefined} | ||
*/ | ||
export const removeNulls = (obj: Record<number | symbol | string, any>) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dnull is recursive. Does this one have to be too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh darn, maybe it should.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated it and added a test, but I need to think more about the use cases for this a little. Nothing is ever easy.....
@@ -22,11 +24,11 @@ export type Scalars = { | |||
Int: number; | |||
Float: number; | |||
BigInt: number; | |||
Date: string; | |||
DateTime: string; | |||
Date: Date | string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was curious to see what you decided to do here 🙂 Why did you have to keep the possibility of this being a string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I explained it in a comment, but it got "outdated"
Important change.
Redwood has built-in handling for Dates (because we use graphql-scalars.dev/docs/scalars/date-time configured) - but our types were only allowing string. This was a misconfiguration, but not super visible, because we were mapping Prisma types anyway.
For DateTime fields you can send back Date but also a valid date-string. Note that when you set DateTime in your SDL, even if you format it yourself, it will convert it back to the RFC3339.
Note also that the code here only affects types we generate, I'm just highlighting what the behaviour actually is and updating the types to match it!
Co-authored-by: Tobbe Lundberg <tobbe@tlundberg.com>
@dac09 tried to help you out by handling the merge conflict in a snapshot file due to the jest 29 upgrade; let me know if t looks like I screwed something up and can revert. |
This reverts commit 05645a0.
@@ -35,3 +35,26 @@ export function normalizeRequest(event: APIGatewayProxyEvent): Request { | |||
body, | |||
} | |||
} | |||
|
|||
// Internal note: Equivalent to dnull package on npm, which seems to have import issues in latest versions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got a build shipped for this: brielov/dnull#65
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Orta! We might switch to dnull internally and keep the removeNulls export
This PR is a combination of fixes (for bugs I introduced during the strict-mode PR) and new features
What does this do?
In v2, we started mapping Prisma types (#5216) to
QueryResolvers
andMutationResolvers
. This was a great improvement, but it required that all services return the full Prisma model and had a few other downsides. This PR addresses these:A. Introduces a new utility type
MergePrismaWithSdlTypes<PrismaUser, User>
which is used in codegen to do the mapping. Which achieves the following cases:1. Return a subset of the fields 🆕
Let's say I modify my SDL to only return a subset of the fields in
Post
. In this case, I never want to return thecreatedAt
property from GraphQlexport const schema = gql` type Post { id: Int! title: String! body: String! - createdAt: DateTime }
The Resolver types used to error out if I do not return
createdAt
in the resolver.What's happening here is that SDL type gets updated, but the Prisma type doesn't (obviously, because we are mapping it, and the field still exists in the DB). The new helper will "pick" all the fields that are common in both the Prisma and the SDL
Post
type.2. Return extra fields 🆕
In the opposite case, I'd like to return extra properties from my GraphQL API, that does not exist in the database/Prisma model. e.g.
export const schema = gql` type Post { id: Int! title: String! body: String! + sdlOnlyProperty: String! }
The generated types now take this into account.
3. Making certain fields optional in SDL, but still keeping them requried in Prisma schema 🆕
This will be handled as well, and your resolvers will not be required to return a body.
B Relation types 🆕
Before this PR, we didn't provide types for relations.
What happens now, in this example, we provide the types for Post.author, but we make this property optional, because its a relation. Redwood generates relation resolvers at the bottom of a service file
Making it optional solves the "infinite" nesting problem with graphql types i.e.
Posts.author.posts.author.posts....
- but you still get type safety if you want to return the relation directly from your service, and not from the relation resolversB. Introduces
removeNulls
utility function 🆕See relevant sections in the doc:
This is because a null in graphql is not equivalent to null in Prisma.