FFFF feat(schema): Initial version of schema definitions and resolvers by daffl · Pull Request #2441 · feathersjs/feathers · GitHub
[go: up one dir, main page]

Skip to content

feat(schema): Initial version of schema definitions and resolvers#2441

Merged
daffl merged 11 commits intodovefrom
schema
Sep 15, 2021
Merged

feat(schema): Initial version of schema definitions and resolvers#2441
daffl merged 11 commits intodovefrom
schema

Conversation

@daffl
Copy link
Member
@daffl daffl commented Aug 30, 2021

This pull request implements the first version of @feathersjs/schema as discussed in #2312. It features schema definitions based on JSON schema with static TypeScript type inference as well as a dynamic resolver system. Specifically:

  • Schemas are used for data type definitions and simple (synchronous) validations
  • Resolvers are used for modifying data based on the context like
    • Populating associations
    • Securing queries and e.g. limiting requests to a user
    • Removing protected properties for external requests or users without the right permissions
    • Hashing passwords and validating dynamic password policies etc.

There may be further iterations necessary but I'm planning on including it in the next pre-release because it is required for further CLI work (done in #2425).

Here is an example for a user schema definition and resolvers with protected passwords and query limitations (non-admins can only see and change their own data):

import {
  schema, resolve, Infer, resolveResult,
  propertyQuery, validateQuery, resolveQuery
} from '@feathersjs/schema';

export const userSchema = schema({
  type: 'object',
  additionalProperties: false,
  required: ['email', 'password'],
  properties: {
    id: { type: 'number' },
    email: { type: 'string' },
    password: { type: 'string' }
  }
} as const);

export type User = Infer<typeof userSchema>;

export const userDataResolver = resolve<User, HookContext<Application>>({
  properties: {
    password: async (value) => {
      // Return a hashed version of the password before storing it in the database
      return bcrypt(value);
    }
  }
});

export const userResultResolver = resolve<User, HookContext<Application>>({
  properties: {
    password: async (value, _user, context) => {
      // Do not return the password for external requests
      return context.params.provider ? undefined : value;
    }
  }
});

export const userQuerySchema = schema({
  type: 'object',
  additionalProperties: false,
  properties: {
    $limit: {
      type: 'number',
      minimum: 0,
      maximum: 100
    },
    $skip: {
      type: 'number'
    },
    // This will allow all of the Feathers query syntax
    id: propertyQuery({
      type: 'number'
    })
  }
} as const);

export type UserQuery = Infer<typeof userQuerySchema>;

export const userQueryResolver = resolve<User, HookContext<Application>>({
  properties: {
    id: async (value, _user, context) => {
      // Non admins can only query and modify their own data
      if(context.params?.user && context.params.user.role !== 'admin') {
        return context.params.user.id;
      }

      return value;
    }
  }
});

app.service('users').hooks([
  validateQuery(userQuerySchema),
  resolveQuery(userQueryResolver),
  resolveResult(userResultResolver
]);

@daffl daffl marked this pull request as ready for review September 5, 2021 01:07
@daffl daffl merged commit c57a5cd into dove Sep 15, 2021
@daffl daffl deleted the schema branch September 15, 2021 04:23
@deskoh
Copy link
Contributor
deskoh commented Sep 21, 2021

@daffl Is it possible to allow a singleton ajv instance to be configured? Custom AJV instance would be useful for options like removeAdditional and can be used for data sanitization (e.g. const sanitizedData = myschema.validate(data)).

@daffl
Copy link
Member Author
daffl commented Jan 12, 2022

@deskoh Passing a custom AJV instance is not possible and documented in https://dove.docs.feathersjs.com/api/schema/schema.html#options

6880

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0