ardatan / ardatan/graphql-tools

wrapSchema transforms the response when error path is not provided

Open
#3,880 2 comments 2 reactions 0 assignees View on GitHub
stage/1-reproduction
Dominant language
TypeScript
Stars
5.4k
Forks
830
Avg merge
10h 59m
Merged PRs (30d)
45

Description

**Describe the bug**

The `wrapSchema` function transforms the response when error path is not provided.
Currently, `@apollo/gateway` doesn't provide this top-level path, so when we wrap a federated graph, responses with errors are not handled correctly.

See https://github.com/apollographql/federation/issues/354

**To Reproduce**

Here a test suite to reproduce the issue:
```typescript
import { makeExecutableSchema } from '@graphql-tools/schema'
import { wrapSchema } from '@graphql-tools/wrap'
import { graphql } from 'graphql'

function generateResponse(withPath = false): Record {
return {
errors: [
{
message: 'Country not found',
path: withPath ? ['companies', 1, 'country', 'name'] : undefined,
},
],
data: {
companies: [
{
country: null,
},
{
country: {
name: null,
},
},
{
country: {
name: 'France',
},
},
],
},
}
}

const typeDefs = `
type Country {
code: String!
name: String
}

type Company {
country: Country
}

type Query {
companies: [Company!]!
}
`

const resolvers = {
Country: {
name(parent: { code: string }) {
switch (parent.code) {
case 'FR':
return 'France'
case 'US':
return 'United States'
default:
throw new Error('Country not found')
}
},
},
Query: {
companies() {
return [{ country: null }, { country: { code: 'BE' } }, { country: { code: 'FR' } }]
},
},
}

const companySchema = makeExecutableSchema({ resolvers, typeDefs })

it('should success with companySchema', async () => {
const response = await graphql(
companySchema,
`
{
companies {
country {
name
}
}
}
`,
)

expect(response.data?.companies[0].country).toBe(null)
expect(response.data?.companies[1].country).toEqual({ name: null })
expect(response.errors).toBeDefined()
})

it('should success by wrapping companySchema when path is provided', async () => {
const wrappedCompanySchema = wrapSchema({
executor() {
return generateResponse(true)
},
schema: companySchema,
})

const response = await graphql(
wrappedCompanySchema,
`
{
companies {
country {
name
}
}
}
`,
)

expect(response.data?.companies[0].country).toBe(null)
expect(response.data?.companies[1].country).toEqual({ name: null })
expect(response.errors).toBeDefined()
})

it('should success by wrapping companySchema when path is not provided', async () => {
const wrappedCompanySchema = wrapSchema({
executor() {
return generateResponse(false)
},
schema: companySchema,
})

const response = await graphql(
wrappedCompanySchema,
`
{
companies {
country {
name
}
}
}
`,
)

console.log(JSON.stringify(response, null, 2))

expect(response.data?.companies[0].country).toBe(null)
expect(response.data?.companies[1].country).toEqual({ name: null })
expect(response.errors).toBeDefined()
})
```

**Current behavior**

When the error doesn't provide the path, we receive:
```javascript
{
"data": {
"companies": [
{
"country": {
"name": null
}
},
{
"country": {
"name": null
}
},
{
"country": {
"name": "France"
}
}
]
}
}
```

Is this behavior is wanted?

**Expected behavior**

We should probably receive:
```javascript
{
"errors": [{ "message": "Country not found" }],
"data": {
"companies": [
{
"country": null
},
{
"country": {
"name": null
}
},
{
"country": {
"name": "France"
}
}
]
}
}
```

**Environment**

- @graphql-tools/schema to 8.3.1
- @graphql-tools/wrap to 8.3.2
- graphql to 15.7.2

Thanks in advance 🙏 Let me know if you need more details.

Contributor guide

Open the contributing guide

Research direction

Start at the wrapSchema entry point and run the reproduction tests described in the issue, comparing behavior when the error path is present and absent. Done means the no-path case preserves the error and produces the expected null placement in the response, alongside the existing path-provided and direct-schema cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.