8000 docs: Improve explanation for queryResolver by AshotN · Pull Request #2956 · feathersjs/feathers · GitHub
[go: up one dir, main page]

Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions docs/api/schema/resolvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,12 @@ In order to get the safe data from resolved associations **all services** involv

Query resolvers use the `hooks.resolveQuery(...resolvers)` hook to modify `params.query`. This is often used to set default values or limit the query so a user can only request data they are allowed to see. It is possible to pass multiple resolvers which will run in the order they are passed, using the previous data. `schemaHooks.resolveQuery` can be used as an `around` or `before` hook.

In this example for a `User` schema we are first checking if a user is available in our request. In the case a user is available we are ruturning the user's ID. Otherwise we return whatever value was provided for `id`.

`context.params.user` would only be set if the request contains a user. This is usually the case when an external request is made. In the case of an internal request we may not have a specific user we are dealing with, and we will just return `value`.

If we were to receive an internal request, such as `app.service('users').get(123)`, `context.params.user` would be `undefined` and we would just return the `value` which is `123`.

```ts
import { hooks as schemaHooks, resolve } from '@feathersjs/schema'
import { Type } from '@feathersjs/typebox'
Expand Down Expand Up @@ -363,3 +369,47 @@ app.service('users').hooks({
}
})
```

For a more complicated example. We will make a separate `queryResolver`, called `companyFilterQueryResolver`, that will act as a ownership filter. We will have a `Company` service that is owned by a `User`. We will assume our app has two registered users and two companies. Each user owning one company. For simplicity, `User1` owns `Company1`, and `User2` owns `Company2`

We want to make sure only the user that owns the company can make any requests related to it. Our schema contains a `ownerUser` field, this is the owner of the company. When a request is made to the company schema, we are effectivly filtering our search for companies to be only those whose `ownerUser` matches the requesting user's id.

So if a `GET /company` request is made by `User1`, our resolver will convert our query to `GET /company?name=Company1&ownerUser={User1.id}`. The result will only return an array of 1 company to `User1`

Similarily, if a patch request was made by `User1` to modify `Company2`. A `404` would occur, as resulting query would search the database for a `Company2` that is owned by `User1` which does not exist.

```ts
// Main data model schema
export const companySchema = Type.Object(
{
id: Type.String({ format: 'uuid' }),
name: Type.String(),
ownerUser: Type.Ref(userSchema)
},
{ $id: 'Company', additionalProperties: false }
)

// Schema for allowed query properties
export const companyQueryProperties = Type.Pick(companySchema, ['id'])
export const companyQuerySchema = Type.Intersect(
[
querySyntax(companyQueryProperties),
// Add additional query properties here
Type.Object({}, { additionalProperties: false })
],
{ additionalProperties: false }
)
export type CompanyQuery = Static<typeof companyQuerySchema>
export const companyQueryValidator = getValidator(companyQuerySchema, queryValidator)
export const companyQueryResolver = resolve<CompanyQuery, HookContext>({})

export const companyFilterQueryResolver = resolve<Company, HookContext>({
ownerUser: async (value, obj, context) => {
if (context.params.user) {
return context.params.user.id
}
return value
}
})
```

0