Skip to content

Commit af18cb4

Browse files
toiroakrclaude
andcommitted
feat(sdk): add toResolverOutput function for TailorDBType conversion
toResolverOutput() converts a TailorDBType to a resolver output type that matches the TailorDB auto-generated query's return type. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 0180437 commit af18cb4

4 files changed

Lines changed: 81 additions & 1 deletion

File tree

.changeset/funny-planes-admire.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@tailor-platform/sdk": minor
3+
---
4+
5+
Add `toResolverOutput` function to convert TailorDBType to resolver output field
6+
7+
- `toResolverOutput(type)` converts a TailorDBType to generated query output type
8+
- Simplifies using TailorDB types as resolver outputs with proper type names

packages/sdk/docs/services/resolver.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,62 @@ createResolver({
120120
});
121121
```
122122

123+
## Custom Type Names
124+
125+
### Using `typeName()` for nested objects
126+
127+
When defining nested objects in input or output schemas, you can specify a custom GraphQL type name using the `typeName()` method. This is useful when you want to control the exact type name that appears in the GraphQL schema.
128+
129+
```typescript
130+
createResolver({
131+
name: "createProfile",
132+
operation: "mutation",
133+
input: {
134+
profile: t
135+
.object({
136+
name: t.string(),
137+
email: t.string(),
138+
})
139+
.typeName("ProfileInput"), // GraphQL type will be "ProfileInput"
140+
},
141+
body: (context) => context.input.profile,
142+
output: t
143+
.object({
144+
name: t.string(),
145+
email: t.string(),
146+
})
147+
.typeName("ProfileOutput"), // GraphQL type will be "ProfileOutput"
148+
});
149+
```
150+
151+
Without `typeName()`, the SDK generates type names automatically (e.g., `CreateProfileProfile` for input).
152+
153+
### Using `toResolverOutput()` for TailorDB types
154+
155+
`toResolverOutput()` makes the resolver's output type match the TailorDB auto-generated query's return type. For example, if you have a `User` type in TailorDB, `toResolverOutput(user)` produces the same GraphQL type as the auto-generated `user` and `users` queries.
156+
157+
```typescript
158+
import { createResolver, t, toResolverOutput } from "@tailor-platform/sdk";
159+
import { user } from "../tailordb/user";
160+
161+
export default createResolver({
162+
name: "getUser",
163+
operation: "query",
164+
input: {
165+
id: t.uuid(),
166+
},
167+
body: async (context) => {
168+
const db = getDB("tailordb");
169+
return await db
170+
.selectFrom("User")
171+
.selectAll()
172+
.where("id", "=", context.input.id)
173+
.executeTakeFirstOrThrow();
174+
},
175+
output: toResolverOutput(user), // Same type as TailorDB's `user` query returns
176+
});
177+
```
178+
123179
## Input Validation
124180

125181
Add validation rules to input fields using the `validate` method:

packages/sdk/src/configure/services/resolver/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { createResolver } from "./resolver";
1+
export { createResolver, toResolverOutput } from "./resolver";
22

33
export type {
44
QueryType,

packages/sdk/src/configure/services/resolver/resolver.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { t } from "@/configure/types/type";
2+
import type { TailorDBType, TailorAnyDBField } from "@/configure/services/tailordb/schema";
23
import type { TailorAnyField, TailorUser } from "@/configure/types";
34
import type { TailorEnv } from "@/configure/types/env";
45
import type { InferFieldsOutput, output } from "@/configure/types/helpers";
@@ -78,3 +79,18 @@ export function createResolver<
7879
// A loose config alias for userland use-cases
7980
// oxlint-disable-next-line no-explicit-any
8081
export type ResolverConfig = ReturnType<typeof createResolver<any, any>>;
82+
83+
/**
84+
* Convert a TailorDBType to a TailorField for use as resolver output.
85+
* Equivalent to: t.object(type.fields).typeName(type.name)
86+
* @param type - The TailorDBType to convert
87+
* @returns A TailorField with the type's fields and name as typeName
88+
*/
89+
export function toResolverOutput<Fields extends Record<string, TailorAnyDBField>>(
90+
type: TailorDBType<Fields>,
91+
): TailorField<{ type: "nested"; array: false; typeName: true }, InferFieldsOutput<Fields>> {
92+
return t.object(type.fields).typeName(type.name) as TailorField<
93+
{ type: "nested"; array: false; typeName: true },
94+
InferFieldsOutput<Fields>
95+
>;
96+
}

0 commit comments

Comments
 (0)