Giter Site home page Giter Site logo

feathersjs-ecosystem / feathers-sequelize Goto Github PK

View Code? Open in Web Editor NEW
206.0 15.0 75.0 1.62 MB

A Feathers service adapter for the Sequelize ORM. Supporting MySQL, MariaDB, Postgres, SQLite, and SQL Server

License: MIT License

JavaScript 0.33% TypeScript 99.67%
feathers-service-adapter sequelize feathersjs

feathers-sequelize's Introduction

feathers-sequelize

CI Download Status Discord

Caution: When you're using feathers v4 and want to upgrade to feathers v5, please make sure to read the migration guide.

NOTE: This is the version for Feathers v5. For Feathers v4 use feathers-sequelize v6

A Feathers database adapter for Sequelize, an ORM for Node.js. It supports PostgreSQL, MySQL, MariaDB, SQLite and MSSQL and features transaction support, relations, read replication and more.

Very Important: Before using this adapter you have to be familiar with both, the Feathers Basics and general use of Sequelize. For associations and relations see the associations section. This adapter may not cover all use cases but they can still be implemented using Sequelize models directly in a Custom Feathers service.

npm install --save feathers-sequelize@pre

And one of the following:

npm install --save pg pg-hstore
npm install --save mysql2 // For both mysql and mariadb dialects
npm install --save sqlite3
npm install --save tedious // MSSQL

Important: feathers-sequelize implements the Feathers Common database adapter API and querying syntax. For more information about models and general Sequelize usage, follow up in the Sequelize documentation.

API

new SequelizeService(options)

Returns a new service instance initialized with the given options.

const Model = require('./models/mymodel');
const { SequelizeService } = require('feathers-sequelize');

app.use('/messages', new SequelizeService({ Model }));
app.use('/messages', new SequelizeService({ Model, id, events, paginate }));

Options:

  • Model (required) - The Sequelize model definition
  • id (optional, default: primary key of the model) - The name of the id field property. Will use the first property with primaryKey: true by default.
  • raw (optional, default: true) - Runs queries faster by returning plain objects instead of Sequelize models.
  • Sequelize (optional, default: Model.sequelize.Sequelize) - The Sequelize instance
  • events (optional) - A list of custom service events sent by this service
  • paginate (optional) - A pagination object containing a default and max page size
  • multi (optional) - Allow create with arrays and update and remove with id null to change multiple items. Can be true for all methods or an array of allowed methods (e.g. [ 'remove', 'create' ])
  • operatorMap (optional) - A mapping from query syntax property names to to Sequelize secure operators
  • operators (optional) - A list of additional query parameters to allow (e..g [ '$regex', '$geoNear' ]). Default is the supported operators

params.sequelize

When making a service method call, params can contain an sequelize property which allows to pass additional Sequelize options. This can e.g. be used to retrieve associations. Normally this wil be set in a before hook:

app.service('messages').hooks({
  before: {
    find(context) {
      // Get the Sequelize instance. In the generated application via:
      const sequelize = context.app.get('sequelizeClient');
      const { User } = sequelize.models;

      context.params.sequelize = {
        include: [ User ]
      }

      return context;
    }
  }
});

Other options that params.sequelize allows you to pass can be found in Sequelize querying docs. Beware that when setting a top-level where property (usually for querying based on a column on an associated model), the where in params.sequelize will overwrite your query.

operatorMap

Sequelize deprecated string based operators a while ago for security reasons. Starting at version 4.0.0 feathers-sequelize converts queries securely, so you can still use string based operators listed below. If you want to support additional Sequelize operators, the operatorMap service option can contain a mapping from query parameter name to Sequelize operator. By default supported are:

'$eq',
'$ne',
'$gte',
'$gt',
'$lte',
'$lt',
'$in',
'$nin',
'$like',
'$notLike',
'$iLike',
'$notILike',
'$or',
'$and'
// Find all users with name similar to Dav
app.service('users').find({
  query: {
    name: {
      $like: 'Dav%'
    }
  }
});
GET /users?name[$like]=Dav%

Caveats

Sequelize raw queries

By default, all feathers-sequelize operations will return raw data (using raw: true when querying the database). This results in faster execution and allows feathers-sequelize to interoperate with feathers-common hooks and other 3rd party integrations. However, this will bypass some of the "goodness" you get when using Sequelize as an ORM:

  • custom getters/setters will be bypassed
  • model-level validations are bypassed
  • associated data loads a bit differently
  • ...and several other issues that one might not expect

Don't worry! The solution is easy. Please read the guides about working with model instances.

Working with MSSQL

When using MSSQL as the database, a default sort order always has to be applied, otherwise the adapter will throw an Invalid usage of the option NEXT in the FETCH statement. error. This can be done in your model with:

model.beforeFind(model => model.order.push(['id', 'ASC']))

Or in a hook like this:

module.exports = function (options = {}) {
  return async context => {
    const { query = {} } = context.params;
    // Sort by id field ascending (or any other property you want)
    // See https://docs.feathersjs.com/api/databases/querying.html#sort
    const $sort = { id: 1 };

    context.params.query = {
      $sort: {

      },
      ...query
    }

    return context;
  }
}

Primary keys

All tables used by a feathers-sequelize service require a primary key. Although it is common practice for many-to-many tables to not have a primary key, this service will break if the table does not have a primary key. This is because most service methods require an ID and because of how feathers maps services to URLs.

Example

Here is an example of a Feathers server with a messages SQLite Sequelize Model:

$ npm install @feathersjs/feathers @feathersjs/errors @feathersjs/express @feathersjs/socketio sequelize feathers-sequelize sqlite3

In app.js:

import path from 'path';
import { feathers } from '@feathersjs/feathers';
import express from '@feathersjs/express';
import socketio from '@feathersjs/socketio';

import Sequelize from 'sequelize';
import SequelizeService from 'feathers-sequelize';

const sequelize = new Sequelize('sequelize', '', '', {
  dialect: 'sqlite',
  storage: path.join(__dirname, 'db.sqlite'),
  logging: false
});

const Message = sequelize.define('message', {
  text: {
    type: Sequelize.STRING,
    allowNull: false
  }
}, {
  freezeTableName: true
});

// Create an Express compatible Feathers application instance.
const app = express(feathers());

// Turn on JSON parser for REST services
app.use(express.json());
// Turn on URL-encoded parser for REST services
app.use(express.urlencoded({ extended: true }));
// Enable REST services
app.configure(express.rest());
// Enable Socket.io services
app.configure(socketio());
// Create an in-memory Feathers service with a default page size of 2 items
// and a maximum size of 4
app.use('/messages', new SequelizeService({
  Model: Message,
  paginate: {
    default: 2,
    max: 4
  }
}));
app.use(express.errorHandler());

Message.sync({ force: true }).then(() => {
  // Create a dummy Message
  app.service('messages').create({
    text: 'Message created on server'
  }).then(message => console.log('Created message', message));
});

// Start the server
const port = 3030;

app.listen(port, () => {
  console.log(`Feathers server listening on port ${port}`);
});

Run the example with node app and go to localhost:3030/messages.

Associations

Embrace the ORM

The documentation on Sequelize associations and relations is essential to implementing associations with this adapter and one of the steepest parts of the Sequelize learning curve. If you have never used an ORM, let it do a lot of the heavy lifting for you!

Setting params.sequelize.include

Once you understand how the include option works with Sequelize, you will want to set that option from a before hook in Feathers. Feathers will pass the value of context.params.sequelize as the options parameter for all Sequelize method calls. This is what your hook might look like:

// GET /my-service?name=John&include=1
function (context) {
  const { include, ...query } = context.params.query;

   if (include) {
      const AssociatedModel = context.app.services.fooservice.Model;
      context.params.sequelize = {
         include: [{ model: AssociatedModel }]
      };
      // Update the query to not include `include`
      context.params.query = query;
   }

   return context;
}

Underneath the hood, feathers will call your models find method sort of like this:

// YourModel is a sequelize model
const options = Object.assign({ where: { name: 'John' }}, context.params.sequelize);
YourModel.findAndCount(options);

For more information, follow up up in the Sequelize documentation for associations and this issue.

Querying

Additionally to the common querying mechanism this adapter also supports all Sequelize query operators.

Note: This adapter supports an additional $returning parameter for patch and remove queries. By setting params.$returning = false it will disable feathers and sequelize from returning what was changed, so mass updates can be done without overwhelming node and/or clients.

Querying a nested column

To query based on a column in an associated model, you can use Sequelize's nested column syntax in a query. The nested column syntax is considered an operator by Feathers, and so each such usage has to be whitelisted.

Example:

// Find a user with post.id == 120
app.service('users').find({
  query: {
    '$post.id$': 120,
    include: {
      model: posts
    }
  }
});

For this case to work, you'll need to add '$post.id$' to the service options' 'whitelist' property.

Working with Sequelize Model instances

It is highly recommended to use raw queries, which is the default. However, there are times when you will want to take advantage of Sequelize Instance methods. There are two ways to tell feathers to return Sequelize instances:

  1. Set { raw: false } in a "before" hook:

    function rawFalse(context) {
        if (!context.params.sequelize) context.params.sequelize = {};
        Object.assign(context.params.sequelize, { raw: false });
        return context;
    }
    hooks.before.find = [rawFalse];
  2. Use the new hydrate hook in the "after" phase:

    const hydrate = require('feathers-sequelize/hooks/hydrate');
    hooks.after.find = [hydrate()];
    
    // Or, if you need to include associated models, you can do the following:
     function includeAssociated (context) {
         return hydrate({
            include: [{ model: context.app.services.fooservice.Model }]
         }).call(this, context);
     }
     hooks.after.find = [includeAssociated];

For a more complete example see this gist.

Important: When working with Sequelize Instances, most of the feathers-hooks-common will no longer work. If you need to use a common hook or other 3rd party hooks, you should use the "dehydrate" hook to convert data back to a plain object:

const { dehydrate, hydrate } = require('feathers-sequelize');
const { populate } = require('feathers-hooks-common');

hooks.after.find = [hydrate(), doSomethingCustom(), dehydrate(), populate()];

Validation

Sequelize by default gives you the ability to add validations at the model level. Using an error handler like the one that comes with Feathers your validation errors will be formatted nicely right out of the box!

Testing sequelize queries in isolation

If you wish to use some of the more advanced features of sequelize, you should first test your queries in isolation (without feathers). Once your query is working, you can integrate it into your feathers app.

1. Build a test file

Creat a temporary file in your project root like this:

// test.js
const app = require('./src/app');
// run setup to initialize relations
app.setup();
const seqClient = app.get('sequelizeClient');
const SomeModel = seqClient.models['some-model'];
const log = console.log.bind(console);

SomeModel.findAll({
   /*
    * Build your custom query here. We will use this object later.
    */
}).then(log).catch(log);

And then run this file like this:

node test.js

Continue updating the file and running it until you are satisfied with the results.

2. Integrate the query using a "before" hook

Once your have your custom query working to your satisfaction, you will want to integrate it into your feathers app. Take the guts of the findAll operation above and create a "before" hook:

function buildCustomQuery(context) {
	context.params.sequelize = {
       /*
        * This is the same object you passed to "findAll" above.
        * This object is *shallow merged* onto the underlying query object
        * generated by feathers-sequelize (it is *not* a deep merge!).
        * The underlying data will already contain the following:
        *   - "where" condition based on query paramters
        *   - "limit" and "offset" based on pagination settings
        *   - "order" based $sort query parameter
        * You can override any/all of the underlying data by setting it here.
        * This gives you full control over the query object passed to sequelize!
        */
	};
}

someService.hooks({
	before: {
		find: [buildCustomQuery]
	}
});

Migrations

Migrations with feathers and sequelize are quite simple. This guide will walk you through creating the recommended file structure, but you are free to rearrange things as you see fit. The following assumes you have a migrations folder in the root of your app.

Initial Setup: one-time tasks

npm install sequelize-cli --save -g
  • Create a .sequelizerc file in your project root with the following content:
const path = require('path');

module.exports = {
  'config': path.resolve('migrations/config.js'),
  'migrations-path': path.resolve('migrations/scripts'),
  'seeders-path': path.resolve('migrations/seeders'),
  'models-path': path.resolve('migrations/models.js')
};
  • Create the migrations config in migrations/config.js:
const app = require('../src/app');
const env = process.env.NODE_ENV || 'development';
const dialect = 'postgres'; // Or your dialect name

module.exports = {
  [env]: {
    dialect,
    url: app.get(dialect),
    migrationStorageTableName: '_migrations'
  }
};
  • Define your models config in migrations/models.js:
const Sequelize = require('sequelize');
const app = require('../src/app');
const sequelize = app.get('sequelizeClient');
const models = sequelize.models;

// The export object must be a dictionary of model names -> models
// It must also include sequelize (instance) and Sequelize (constructor) properties
module.exports = Object.assign({
  Sequelize,
  sequelize
}, models);

Migrations workflow

The migration commands will load your application and it is therefore required that you define the same environment variables as when running your application. For example, many applications will define the database connection string in the startup command:

DATABASE_URL=postgres://user:pass@host:port/dbname npm start

All of the following commands assume that you have defined the same environment variables used by your application.

ProTip: To save typing, you can export environment variables for your current bash/terminal session:

export DATABASE_URL=postgres://user:pass@host:port/db

Create a new migration

To create a new migration file, run the following command and provide a meaningful name:

sequelize migration:create --name="meaningful-name"

This will create a new file in the migrations/scripts folder. All migration file names will be prefixed with a sortable data/time string: 20160421135254-meaningful-name.js. This prefix is crucial for making sure your migrations are executed in the proper order.

NOTE: The order of your migrations is determined by the alphabetical order of the migration scripts in the file system. The file names generated by the CLI tools will always ensure that the most recent migration comes last.

Add the up/down scripts:

Open the newly created migration file and write the code to both apply and undo the migration. Please refer to the sequelize migration functions for available operations. Do not be lazy - write the down script too and test! Here is an example of converting a NOT NULL column accept null values:

'use strict';

module.exports = {
  up: function (queryInterface, Sequelize) {
    return queryInterface.changeColumn('tableName', 'columnName', {
      type: Sequelize.STRING,
      allowNull: true
    });
  },

  down: function (queryInterface, Sequelize) {
    return queryInterface.changeColumn('tableName', 'columnName', {
      type: Sequelize.STRING,
      allowNull: false
    });
  }
};

ProTip: As of this writing, if you use the changeColumn method you must always specify the type, even if the type is not changing.

ProTip: Down scripts are typically easy to create and should be nearly identical to the up script except with inverted logic and inverse method calls.

Keeping your app code in sync with migrations

The application code should always be up to date with the migrations. This allows the app to be freshly installed with everything up-to-date without running the migration scripts. Your migrations should also never break a freshly installed app. This often times requires that you perform any necessary checks before executing a task. For example, if you update a model to include a new field, your migration should first check to make sure that new field does not exist:

'use strict';

module.exports = {
  up: function (queryInterface, Sequelize) {
    return queryInterface.describeTable('tableName').then(attributes => {
      if ( !attributes.columnName ) {
        return queryInterface.addColumn('tableName', 'columnName', {
          type: Sequelize.INTEGER,
          defaultValue: 0
        });
      }
    })
  },

  down: function (queryInterface, Sequelize) {
    return queryInterface.describeTable('tableName').then(attributes => {
      if ( attributes.columnName ) {
        return queryInterface.removeColumn('tableName', 'columnName');
      }
    });
  }
};

Apply a migration

The CLI tools will always run your migrations in the correct order and will keep track of which migrations have been applied and which have not. This data is stored in the database under the _migrations table. To ensure you are up to date, simply run the following:

sequelize db:migrate

ProTip: You can add the migrations script to your application startup command to ensure that all migrations have run every time your app is started. Try updating your package.json scripts attribute and run npm start:

scripts: {
    start: "sequelize db:migrate && node src/"
}

Undo the previous migration

To undo the last migration, run the following command:

sequelize db:migrate:undo

Continue running the command to undo each migration one at a time - the migrations will be undone in the proper order.

Note: - You shouldn't really have to undo a migration unless you are the one developing a new migration and you want to test that it works. Applications rarely have to revert to a previous state, but when they do you will be glad you took the time to write and test your down scripts!

Reverting your app to a previous state

In the unfortunate case where you must revert your app to a previous state, it is important to take your time and plan your method of attack. Every application is different and there is no one-size-fits-all strategy for rewinding an application. However, most applications should be able to follow these steps (order is important):

  1. Stop your application (kill the process)
  2. Find the last stable version of your app
  3. Count the number of migrations which have been added since that version
  4. Undo your migrations one at a time until the db is in the correct state
  5. Revert your code back to the previous state
  6. Start your app

Migrating

feathers-sequelize 4.0.0 comes with important security and usability updates.

Important: For general migration information to the new database adapter functionality see crow.docs.feathersjs.com/migrating.html#database-adapters.

The following breaking changes have been introduced:

  • All methods now take params.sequelize into account
  • All methods allow additional query parameters
  • Multiple updates are disabled by default (see the multi option)
  • Upgraded to secure Sequelize operators (see the operators option)
  • Errors no longer contain Sequelize specific information. The original Sequelize error can be retrieved on the server via:
const { ERROR } = require('feathers-sequelize');

try {
  await sequelizeService.doSomethign();
} catch(error) {
  // error is a FeathersError
  // Safely retrieve the Sequelize error
  const sequelizeError = error[ERROR];
}

License

Copyright (c) 2022

Licensed under the MIT license.

Migrate to Feathers v5 (dove)

There are several breaking changes for feathers-sequelize in Feathers v5. This guide will help you to migrate your existing Feathers v4 application to Feathers v5.

Named export

The default export of feathers-sequelize has been removed. You now have to import the SequelizeService class directly:

const { SequelizeService } = require('feathers-sequelize');

app.use('/messages', new SequelizeService({ ... }));

This follows conventions from feathers v5.

operators / operatorMap

Feathers v5 introduces a convention for options.operators and options.filters. The way feathers-sequelize worked in previous version is not compatible with these conventions. Please read https://dove.feathersjs.com/guides/migrating.html#custom-filters-operators first.

The old options.operators object is renamed to options.operatorMap:

const { SequelizeService } = require('feathers-sequelize');
const { Op } = require('sequelize');

app.use('/messages', new SequelizeService({
  Model,
  // operators is now operatorMap:
  operatorMap: {
    $between: Op.between
  }
}));

The new options.operators option is an array of allowed operators.

filters

Feathers v5 introduces a convention for options.operators and options.filters. The way feathers-sequelize worked in previous version is not compatible with these conventions. Please read https://dove.feathersjs.com/guides/migrating.html#custom-filters-operators first.

Feathers v5 introduces a new filters option. It is an object to verify filters. Here you need to add $dollar.notation$ operators, if you have some.

const { SequelizeService } = require('feathers-sequelize');

app.use('/messages', new SequelizeService({
  Model,
  filters: {
    '$and': true,
    '$person.name$': true
  }
}));

whitelist

Feathers v5 introduces a convention for options.operators and options.filters. The way feathers-sequelize worked in previous version is not compatible with these conventions. Please read https://dove.feathersjs.com/guides/migrating.html#custom-filters-operators.

feathers-sequelize's People

Contributors

andrewjdr avatar andymoon avatar bravo-kernel avatar buske avatar corymsmith avatar daddywarbucks avatar daffl avatar designbyonyx avatar devel-pa avatar edwardsmarkf avatar eikaramba avatar ekryski avatar fratzinger avatar greenkeeper[bot] avatar greenkeeperio-bot avatar honzahommer avatar lowip avatar lubomirigonda1 avatar mariacamilacubides avatar marshallswain avatar michaelermer avatar msimulcik avatar murbanowicz avatar nicholasnelson avatar omeid avatar pyvkd avatar rafalszemraj avatar ryanthegiantlion avatar silvestreh avatar timnz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

feathers-sequelize's Issues

Allow custom primary keys

I have changed a service's primary key to a custom one through id's property, however the change didn't affect the queries as they were still using the id key. I think this is caused by

line 124 in lib/index.js
var q = _extends({ where: _extends({ id: id }, where) }, params.sequelize);

Return a 400 error for invalid queries

Steps to reproduce

Ref: StackOverflow Question

Client performs an invalid GET request:
eg: http://localhost:3030/stations/?nonsensesearchquery

Expected behavior

Feathers should return a 400 Bad Request and a message that doesn't contain DB/SQL details

Actual behavior

A 500 error is returned:
ER_BAD_FIELD_ERROR: Unknown column 'stations.nonsensesearchquery' in 'where clause'

System configuration

App generated from current feathers-cli (v1.2.7)


I'm currently using the following error hook to throw a nicer error and posting this as an issue as suggested by @daffl on the SO question

function catchSequelizeError(hook) {
  if(hook.error.original.code, 'ER_BAD_FIELD_ERROR') { 
    hook.error = new errors.BadRequest('Invalid search query');
  }
}

Suggested pattern to create a multi model service

I have multiple models so I thought I could add a service for each of them. Like so:

Object.keys(models).map(key => app.use(`/api/${key}`, sequelize({ Model: db[key] })))

But this seems like an inelegant approach if I want to have consistent hooks and other behavior for all of them.

I've looked at another approach which generates a service at the time of a request:

app.use('/api/:path', (req) => sequelize({ Model: db[req.params.path] }))

The above doesn't seem to work actually, but the conceptual point stands. Even with this approach it's not very clear how to attach events to this freshly created temp instance of a service. Not even sure if the app will retain all these services or clear them out after a single usage. Plus it might not be very performant either.

I guess what I'm looking for is a parent service that can modularly connect to various models and provides a consistent surface like any other feathers service.

find total is always zero when raw is false

Steps to reproduce

I have a 3 models. Product, Shop and Branch.

  • A product only belongs to a shop.
  • A shop has many branches.

I am trying to add this in the find before hook

const sequelize = hook.app.get('sequelizeClient');
hook.params.sequelize = { raw: false, include: [ sequelize.models.shops, { model: sequelize.models['branch-products'], include: [{ model: sequelize.models.branches}] } ] };

Actual behavior

When I add the raw: false, the total is always 0.
Also, when I add where in the query. It gives repeated objects.

Find() generates an invalid query with pagination but no order.

Right now the find method will generate an invalid SQL statement when pagination defaults are set or provided by the query and an order\sort value is not (as is created by the feathersjs generators). The generated SQL has FETCH NEXT and OFFSET but no order by. Sequelize is supposed to handle this by defaulting to the Model's primary key field, but because feathers-sequelize always provides at least an empty array for the order filter in it's query, it fails. A simple early out here /src/utils.js#L36 to return null or undefined resolves the issue. Or it could be fixed here /src/index.js#L30 by checking for the empty array before adding it to the query. Any preferences?

Avoid findAndCount if paginate is false

Is there a reason why findAndCount is always used and the returned object contains all counts etc. EVEN if pagination is deactivated. I think this makes a good optimization improvement to only use sequelize find() method and reduce half of the calls in this case

Custom id column name

Hey

Service:

const service = require('feathers-sequelize')
const hooks = require('./hooks')

export default function () {
  const app = this
  const models = app.get('sequelize').models

  const options = {
    id: 'uuid',
    Model: models.task
  }

  // Initialize our service with any options it requires
  app.use('/tasks', service(options))

  // Get our initialize service to that we can bind hooks
  const taskService = app.service('/tasks')

  // Set up our before hooks
  taskService.before(hooks.before)

  // Set up our after hooks
  taskService.after(hooks.after)
}

Model:

import Sequelize from 'sequelize'

export default function () {
  let sequelize = this.get('sequelize')

  const Task = sequelize.define('task', {
    uuid: {
      type: Sequelize.UUID,
      unique: true,
      isUUID: 4,
      defaultValue: Sequelize.UUIDV4
    },
    userId: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    workspaceId: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    content: {
      type: Sequelize.STRING,
      allowNull: false
    },
    isCompleted: {
      type: Sequelize.BOOLEAN,
      defaultValue: 0
    },
    dueDate: {
      type: Sequelize.DATE
    }
  })

  return Task
}

The problem:

Executing (default): UPDATE `tasks` SET `isCompleted`=false,`updatedAt`='2016-05-17 10:50:49' WHERE `uuid` = 'da136d3f-476d-4ebe-8141-f77b58d9e5c5'
Executing (default): SELECT `id`, `uuid`, `userId`, `workspaceId`, `content`, `isCompleted`, `dueDate`, `createdAt`, `updatedAt` FROM `tasks` AS `task` WHERE `task`.`id` = 'da136d3f-476d-4ebe-8141-f77b58d9e5c5';

Patch method after successful update calls getOrFind method. The first query is ok but the second called by getOrFind doesn't use custom id column and generates error.

NotFound: No record found for id '88750834-d5a5-42c4-85c0-56ef93616358' at Object.construct (C:\Users\Kasper\Code\ticklist\server\node_modules\core-js\modules\es6.reflect.construct.js:22:24) at NotFound.ExtendableBuiltin (C:\Users\Kasper\Code\ticklist\server\node_modules\feathers-errors\lib\index.js:21:28) at NotFound.FeathersError (C:\Users\Kasper\Code\ticklist\server\node_modules\feathers-errors\lib\index.js:99:87) at new NotFound (C:\Users\Kasper\Code\ticklist\server\node_modules\feathers-errors\lib\index.js:188:77) at [object Object].<anonymous> (C:\Users\Kasper\Code\ticklist\server\node_modules\feathers-sequelize\lib\index.js:106:17) at [object Object].tryCatcher (C:\Users\Kasper\Code\ticklist\server\node_modules\bluebird\js\release\util.js:16:23) at Promise._settlePromiseFromHandler (C:\Users\Kasper\Code\ticklist\server\node_modules\bluebird\js\release\promise.js:502:31) at Promise._settlePromise (C:\Users\Kasper\Code\ticklist\server\node_modules\bluebird\js\release\promise.js:559:18) at Promise._settlePromise0 (C:\Users\Kasper\Code\ticklist\server\node_modules\bluebird\js\release\promise.js:604:10) at Promise._settlePromises (C:\Users\Kasper\Code\ticklist\server\node_modules\bluebird\js\release\promise.js:683:18) at Async._drainQueue (C:\Users\Kasper\Code\ticklist\server\node_modules\bluebird\js\release\async.js:138:16) at Async._drainQueues (C:\Users\Kasper\Code\ticklist\server\node_modules\bluebird\js\release\async.js:148:10) at Immediate.Async.drainQueues [as _onImmediate] (C:\Users\Kasper\Code\ticklist\server\node_modules\bluebird\js\release\async.js:17:14) at tryOnImmediate (timers.js:534:15) at processImmediate [as _immediateCallback] (timers.js:514:5)

ES6/Babel

Hi,

I cloned, npm installed, and node examples/app using node 4.5.0 LTS. I got a syntax error on "import" keyword. Ok, so this is using ES6 I guess. The README doesn't mention this, though.

How to add the model name in the REST response

Hi David,

First of all, thanks for your code, it looks really interesting and I'm sure it will be heavily useful to me.

I just have a quick newbie question.

I'm building a JS app with Ember and Ember Data for my frontend. I'm using Feathers and Sequelize for my API, hence feathers-sequelize is meant for me.

Ember Data expects that the REST response contains the name of the object. For a call to GET conversations, instead of returning this:

[
{
id: 1,
title: "First conversation",
createdAt: "2016-01-26T19:01:28.466Z",
updatedAt: "2016-01-26T19:01:28.466Z"
}
]

It expects this:

{
conversations: [
{
id: 1,
title: "Test conversation - 1453476145564",
createdAt: "2016-01-22T15:22:25.650Z",
updatedAt: "2016-01-22T15:22:25.650Z"
}
]
}

Is there an easy way to achieve that on Service definition? I thought about creating a handler based on what I read there: http://feathersjs.com/docs/#toc2 But I'm not sure that's the proper way.

Let me know if you need more information. Thanks in advance.

Cheers,

Lionel

update() breaks when sequelize configured with raw:true

Affects master, v1.4.3, etc.

A call to update() results in a Model.findById call:
https://github.com/feathersjs/feathers-sequelize/blob/master/src/index.js#L168

If sequelize is configured with raw: false (the default), this returns a DAO, with a toJSON method, which is then called several lines later:
https://github.com/feathersjs/feathers-sequelize/blob/master/src/index.js#L174

However, if sequelize is configured with raw: true, the findById call returns a JSON object without this method.

There are two simple fixes:

  1. Only call toJSON if it exists and is a function
  2. pass {raw: false} as a second argument to the findById call to force the expected behaviour

Solved: How to add relationship

After several days i don't know what to do anymore. My use case is simple:

After a user is created let's add a default item to his inventory (MxN relationship)

According to the sequelize docs we just call the appropiate prototype function.

Let's say we have an after hook as following:

return function(hook) {
    if(hook.result){                                        //If user creation was successful
      return hook.app.service('items').get(1).then(item=> {        //Find default item ...
        hook.result.addInventory(item);                     //and add it to the user
      });
    }
  };

FYI, this is the model relation definition(vice versa for items):

user.associate = function(models) {
    user.belongsToMany(models.items, { as:'Inventories', through: 'inventories'});
  };

Can we create nested models in one go?

If I want to create multiple models in one go, can I do so?

For example, I have a Customer that has a Contact associated with it.

export default (sequelize, DataTypes) => {
  const Customer = sequelize.define('customer', {
    id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      allowNull: false,
      primaryKey: true
    }
  }, {
    classMethods: {
      associate() {
        const models = sequelize.models;
        Customer.hasOne(models.contact);
      }
    },
    tableName: 'customers'
  });
  return Customer;
};

I run sequelize's associations while importing the models, like so

Object.keys(db)
    .map(name => db[name])
    .filter(model => model.associate)
    .forEach(model => model.associate());

sequelize.sync();

Finally, I have the customer service defined like this

class CustomerSvc extends DBService {
  constructor() {
    super({
      Model: db.customer
    });
  }

  create(data, params) {
    return super.create(data, Object.assign({}, params, { sequelize: { include: [db.contact] } }));
  }
}

export default function() {
  return this.use('/customers', new CustomerSvc());
}

Now, the problem that I am stuck on is creating a contact along with the customer in one go.

For example,

POST /api/customers
{
  "contact": {
    "firstName": "Test",
    "lastName": "Test"
  }
}

I want this to create the contact if it doesn't exist, and fetch and use a contact if the contact ID is passed. But what feathers is doing is only running the query to create the customer without the contact.

This is what I see in the logs.

Executing (default): INSERT INTO "customers" ("id","createdAt","updatedAt") VALUES (DEFAULT,'2017-03-21 14:43:48.195 +00:00','2017-03-21 14:43:40.187 +00:00') RETURNING *;

Please advise on the best way to approach this problem.

only one instance of babel-polyfill is allowed

Apparently one can only require babel once: https://phabricator.babeljs.io/T1019

/home/jakob/devel/cloudio/node_modules/feathers-sequelize/node_modules/babel-polyfill/lib/index.js:8

  throw new Error("only one instance of babel-polyfill is allowed");
  ^

Error: only one instance of babel-polyfill is allowed
    at Object.<anonymous> (/home/jakob/devel/cloudio/node_modules/feathers-sequelize/node_modules/babel-polyfill/lib/index.js:8:9)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)
    at Object.<anonymous> (/home/jakob/devel/cloudio/node_modules/feathers-sequelize/lib/index.js:26:1)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)

Exporting utils.js

Hey guys,

Would be nice if we could access the utils.js file, to use it when extending some of the methods instead of duplicating it. Or maybe it's already possible in another way than going to look for it through the path in node_modules ?

Thanks

store String array in mysql

I'm trying to figure out, how to store a String array in my mysql table. Currently I'm trying to implement feathers-authentication-management and I need to store e.g. ['username', 'email', 'cellphone'] in a column. There is no DataType for mysql to accomplish that, right? So I tried using getters and setters:

const users = sequelizeClient.define('users', {
    verifyChanges: { 
        type: Sequelize.STRING,
        get() {
            return JSON.parse(this.getDataValue('verifyChanges'));
        },
        set(val) {
            this.setDataValue('verifyChanges', JSON.stringify(val));
        }
    },

I'm getting the following error:

Failed to load resource: the server responded with a status of 400 (Bad Request)
Error occurred: Error: string violation: verifyChanges cannot be an array or an object

What is best practice? I'm just starting with feathers, but using the beforeValidate and afterValidate hook to bypass the DataType validation seems odd to me. Thanks for your help!

Discuss querying of NULL values

Dealing with a relational database feathers-sequelize currently lacks the possibility to query for NULL values.

Especially for relations this is a big bummer, as you might want to only query entities where one or more relations is NULL(basically doing set manipulations like "only give entities from A subset B or B union A etc.") or where a attribute is null.

Currently feathers-rest does not handle this case as null is always translated to String "null". However boolean are converted (i haven't looked at the code).

Anyway my current solution is to use a hook that traverses the query object and converts a null value string back to a null value

handleNullQueries: () => hook => {
    let where = Object.assign({}, hook.params.query);

    function transformQuery(obj) {
      Object.keys(obj).forEach(function (prop) {
        let value = obj[prop];
        if (value !== null && typeof value === 'object')
          obj[prop] = transformQuery(value);
        else if (value === 'NULL') //Yes currently i use uppercase null for this, maybe change it to lowercase
          obj[prop] = null;
      });
      return obj;
    }

    hook.params.query = transformQuery(where);
  }

Any opinion about this? I think there are far more use cases FOR having true null values as for the use case that someone needs to transmit a String 'null'.

TypeError: Cannot read property '2' of null

Hello,

So I'm giving FeathersJs a try and so far its supper easy to create quick rest apis. However I am stuck on an issue when creating a duplicate record where the table column is unique. I came across this issue when testing since other application might want to send a POST request to create a record but it "email" for example might already be used.

What you are doing?

Creating a record with the same value in a column that is Unique:true. I successfully inserted new record via restful api using Postman. However if I send the same request again( create a user with the same email which the column is unique) it crashes the server with error "TypeError: Cannot read property '2' of null".

Below is the model I am using

// code here
const Sequelize = require('sequelize');

module.exports = function(sequelize) {
  const user = sequelize.define('users', {
    email: {
      type: Sequelize.STRING,
      allowNull: false,
      unique: true
    },
    password: {
      type: Sequelize.STRING,
      allowNull: false
    }
  }, {
    freezeTableName: true
  });

What do you expect to happen?

I expected to get a response back with an error stating its a duplicate or at least a clean error.

What is actually happening?

Crashed server since error wasn't caught.

Error Image

image

Dialect: mysql
Database version: 5.7.13
Sequelize version: 3.24.4

Error while making a PATCH method call

I was looking to send atomic updates to my server and in doing so required a PATCH call to be made. But the server returned the following error to the client (with errorhandler on).

{
  "name": "GeneralError",
  "message": "column \"id\" does not exist",
  "code": 500,
  "className": "general-error",
  "data": {},
  "errors": {}
}

Without errorhandler this was the error printed on the server console.

SequelizeBaseError: column "id" does not exist                                                                                     
    at Query.formatError (D:\code\playground\core-playground\feathers\node_modules\sequelize\lib\dialects\postgres\query.js:357:14)
    at Query.<anonymous> (D:\code\playground\core-playground\feathers\node_modules\sequelize\lib\dialects\postgres\query.js:88:19) 

Now I was able to make GET PUT calls to the server no problem. But it's only during PATCH is when I'm facing this issue. And I am a bit confused sue to that reason, because PUT must take similar arguments as PATCH.

Any way to diagnose it further?

One possibility is that my primary key in the resource is not named id rather custom named as xyz_id, if this is the issue in fact then any way I can use a hook to pre-process the data and make the patch method work? And also shouldn't this simply work out of the box given the id is already present in the URL to which the call is made /db/:resource/:id and there should be no reliance on the primary key being named id.

Expose Sequelize lib

So that end users don't need to require it separately we should just expose the underlying sequelize lib. Just in case people need access to it.

N:N relation using React Native

Sorry for posting here but I'm really not undertanding how can I make a request(I'm using socket) and get data from a N:N relationship.

I'm using MySQL.

Thank you!

Add example for model relations

Can somebody show an example of creating relations between the models?

I want to relate a model Tutorial and Comment. How can i do that?

I make it so:

'use strict'

const Sequelize = require('sequelize')

module.exports = function (sequelize) {
  const comment = sequelize.define('comment', {
    content: {
      required: true,
      type: Sequelize.TEXT,
      allowNull: false
    },
    tutorialId: {
      required: true,
      type: Sequelize.INTEGER
    }
  }, {
    timestamps: true,
    freezeTableName: true
  })

  comment.belongsTo(sequelize.models.tutorial)

  comment.sync({
    force: true
  })

  return comment
}

But sometimes, when i start my application i have error:

Unhandled rejection SequelizeDatabaseError: relation "tutorial" does not exist

I used to work only with mongoDB. But I think I need to learn sequelize.

It would be great if we had more examples that show how to work with the database, how to set up the relationship between models and between services. I read the documentation, but it very much and I'm not like I can not configure it to work properly.

Removing with query for foreign key equality does not do anything

Steps to reproduce

configurationType model association:

associate({configuration}: any) {
  ConfigurationType.hasMany(configuration)
})

configurationTypeId is foreign key.

const id = 'foo'
const Configuration =app.service('configurations')
Configuration.remove(null, {
  query: {
      configurationTypeId: id
  }
})

Expected behavior

It should remove rows where configurationTypeId column equals id. (As I get it from documentation)

Actual behavior

Does not do anything.

System configuration

Module versions (especially the part that's not working):

"feathers": "2.1.3",
"feathers-sequelize": "2.0.0",

NodeJS version:
7.5.0

Operating System:
MacOS

Make params optional

Some of the methods e.g. get require params be given (by using params.sequelize). This is fine if dealing with an incoming request or via app.service (I believe) but if using the service directly without going through app it'll throw TypeError: Cannot read property 'sequelize' of undefined

Investigate issue where getters, setters not being called

This has bit me before where data did not go through the defined getters/setters where I had some business rules.

Another good use case just came up with someone who wanted to save an array in MySql, which does not support arrays natively like Postgres. They had a property defined like this:

    fieldsToValidate: {
         type: Sequelize.STRING,
         get() {
             return JSON.parse(this.getDataValue('verifyChanges'));
         },
         set(val) {
             this.setDataValue('verifyChanges', JSON.stringify(val));         }
     }

Results in the following error:

Failed to load resource: the server responded with a status of 400 (Bad Request)   
Error occurred:  
    Error: string violation: verifyChanges cannot be an array or an object

How to retrieve records for a many to many relationship?

Hi guys,

Sorry for the questions again ;-)

I have an Intent model that can have many Entities. Entities can also belong to many Intents. Classical many to many relationship.

In my services, I'd like to be able to retrieve the Entities related to an Intent. I want to create a service that responds to an endpoint like "/api/v1/intents/1/entities"

My idea is to create a service called IntentEntity and match it with my Entity model. Now, how is it possible to limit the results to the ones that match the intentId parameter defined in the URL? I mean, there's no intentId attribute in the entities table in my database.

Let me know if you need more information.

Thanks in advance for your help.

Cheers,

Lionel

updated_at column does not get updated at PATCH request

Steps to reproduce

  1. Use Postgres
  2. Create a new service with a model with a table
  3. Make sure the model has created_at and updated_at columns and some other column that you can update (patch) afterwards (i.e. note)
return sequelize.define('test_table', {
note: {
      type: Sequelize.TEXT,
      allowNull: false
    },

updatedAt: {
      field: 'updated_at',
      type: Sequelize.DATE,
      allowNull: false,
      defaultValue: Sequelize.NOW
    },

createdAt: {
      field: 'created_at',
      type: Sequelize.DATE,
      allowNull: false,
      defaultValue: Sequelize.NOW
    }
}, {
    // http://docs.sequelizejs.com/en/latest/docs/models-definition/#configuration
    freezeTableName: true,
    timestamps: false
});
  1. Create a new record in the table
  2. Make a PATCH request to update the text column

Expected behavior

The updated_at column should get updated with the time of the update performed (and thus be different from the created_at column value).

Note: in fact I replace the updated_at and created_at with update_time and create_time by renaming the properties in the model definition and then in the options object I add:

    updatedAt: 'updateTime',
    createdAt: 'createTime'

bellow the freezeTableName and timestamps properties.
I've been working with this approach with Sails.js + Sequelize and works fine.

But nevertheless, here I switched to updatedAt and createdAt just to test from the zero.

Actual behavior

The updated_at column does not get updated.

System configuration

Tell us about the applicable parts of your setup.

Module versions (especially the part that's not working):

    "feathers-sequelize": "^1.4.0",
    "sequelize": "^3.24.3"

NodeJS version:
v6.9.4
Operating System:
OS X El Capitan

$populate does not seem to work

Steps to reproduce

Create a linked relationship between two models

Expected behavior

When I query a basic endpoint I expect the $populate parameter to include my related model.

Actual behavior

The related model is not included in my results. The include never gets sent to sequelize and therefore the inner join is never made.

An in-range update of feathers-errors is breaking the build 🚨

Version 2.8.0 of feathers-errors just got published.

Branch Build failing 🚨
Dependency feathers-errors
Current Version 2.7.1
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

feathers-errors is a direct dependency of this project this is very likely breaking your project right now. If other packages depend on you it’s very likely also breaking them.
I recommend you give this issue a very high priority. I’m sure you can resolve this πŸ’ͺ

Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build failed Details

Commits

The new version differs by 3 commits0.

false

See the full diff

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Using `include` breaks the find method query

I have the following n:m models set up:

// User Model
import uid from 'uid-safe';

export default (sequelize, DataTypes) => {
  const User = sequelize.define('user', {
    id: {
      type: DataTypes.STRING,
      defaultValue: () => `usr_${uid.sync(6)}`,
      primaryKey: true
    },
    type: DataTypes.ENUM('customer', 'operator', 'vendor'),
    childId: DataTypes.STRING,
    username: DataTypes.STRING(64),
    password: DataTypes.STRING(512)
  }, {
    classMethods: {
      associate() {
        User.hasOne(sequelize.models.contact);
        User.belongsToMany(sequelize.models.conversation, { through: 'UserConversation' });
      }
    }
  });
  return User;
};
// Conversation Model
import uid from 'uid-safe';

export default (sequelize, DataTypes) => {
  const Conversation = sequelize.define('conversation', {
    id: {
      type: DataTypes.STRING,
      defaultValue: () => `cnv_${uid.sync(6)}`,
      primaryKey: true
    },
    currentMode: DataTypes.ENUM('Web', 'SMS', 'Email', 'Phone')
  }, {
    classMethods: {
      associate() {
        Conversation.hasMany(sequelize.models.message);
        Conversation.belongsToMany(sequelize.models.user, { as: 'members', through: 'UserConversation' });
      }
    }
  });
  return Conversation;
};
// User Conversation Model
export default (sequelize, DataTypes) => sequelize.define('UserConversation', {});

Now if I do a find call on Users it works fine.

app.service('users').find({
  query: {
    username: 'xyz'
  }
})

But if I add a include hook to fetch conversations of a user:

  app.service('users').before({
    all: [
       (hook) => ({
         ...hook,
         params: {
           sequelize: {
             include: [{
               model: db.conversation,
               through: { attributes: [] },
               attributes: ['id']
             }]
           }
         }
      })
    ]
  });

Then the same find method breaks and my query is ignored, returning all users to me and not the ones that match my query.

Relation between Models not working.

Have been trying to follow the demo given here many-to-many-sequelize by @ekryski. But to no avail ☹...

I have two models:

// channel-model.js
import Sequelize from 'sequelize';

export default (sequelize) => {
  const Channel = sequelize.define('Channel', {
    id: {
      type: Sequelize.DataTypes.INTEGER,
      autoIncrement: true,
      allowNull: false,
      primaryKey: true
    },
    name: {
      type: Sequelize.DataTypes.STRING,
      allowNull: false
    },
    createdAt: {
      type: Sequelize.DataTypes.DATE,
      defaultValue: new Date()
    },
    updatedAt: {
      type: Sequelize.DataTypes.DATE,
      defaultValue: new Date()
    }
  }, {
    freezeTableName: true,
    classMethods: {
      associate() {
        Channel.hasOne(sequelize.models.Message, {
          foreignKey: 'id',
          targetKey: 'id',
          as: 'message'
        });
      }
    }
  });

  return Channel;
};
// message-model.js
import Sequelize from 'sequelize';

export default (sequelize) => {
  const Message = sequelize.define('Message', {
    id: {
      type: Sequelize.DataTypes.INTEGER,
      autoIncrement: true,
      allowNull: false,
      primaryKey: true
    },
    text: {
      type: Sequelize.DataTypes.STRING,
      allowNull: false
    },
    createdAt: {
      type: Sequelize.DataTypes.DATE,
      defaultValue: new Date()
    },
    updatedAt: {
      type: Sequelize.DataTypes.DATE,
      defaultValue: new Date()
    }
  }, {
    freezeTableName: true
  });

  return Message;
};

I am able to create both resources independently, but not able to associate a given message to a channel. Here I want any one message to be linked to any channel. All queries being made to the channel just ignore the data and do not modify any relational fields, which btw do not even appear in the resource on the get call.

I'm not sure where the error lies. Maybe in the migrations? But they were not documented in the demo above so not sure if I've written them correctly or not.

Have uploaded my entire code here: feathers-sequelize-poc for further evaluation by everyone.

When i need force sync?

I can not understand how works sync method.

I have read the documentation about it. But I do not understand anything. When I have to do the synchronization? Each time when i restart my application it will be deleted all the data from database.

Or should I synchronize only the first time when i run the application and the database table has not yet been created?

But what if the model schema changes? Do i have to synchronize again?

Maybe anyone can share a link to a tutorial and examples?

Consider creating a sequelize instance during updates:

One of my model properties implements a getter to ensure that an array field always has a value:

notes: {
      type: Sequelize.ARRAY(Sequelize.TEXT),
      get () {
          const val = this.getDataValue('notes');
          return val || [];
      }
}

However, when data is posted to the server, the data is handled in its raw format (see here). If a null value is posted, it gets saved to the db. I can protect against this with allowNull: false, but that generates an error when I'd rather an empty array be saved.

Is it possible to instantiate the data using Model.build before processing it:

data = this.Model.build(data, { isNewRecord: false });

An in-range update of feathers is breaking the build 🚨

Version 2.1.2 of feathers just got published.

Branch Build failing 🚨
Dependency feathers
Current Version 2.1.1
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As feathers is β€œonly” a devDependency of this project it might not break production or downstream projects, but β€œonly” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this πŸ’ͺ

Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build failed Details

Commits

The new version differs by 5 commits0.

  • 377a9c3 2.1.2
  • 3dcac6e Fix typescript defnition of Service. All the service methods should be (#573)
  • 559345b chore(package): update socket.io-client to version 2.0.0 (#572)
  • f6c0dd5 Update dependencies to enable Greenkeeper 🌴 (#551)
  • 521376e Fix pagination type definition. (#527)

false

See the full diff

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Private method _get() not processing parameters

In method .get() it is possible to pass parameters to the query.

get(id, params) {
  return this._get(id, params);
}

For example, so:

app.service('api/v1/users').get('kulakowka', {
  query: {
    $select: {'username': 1}
  }
})

But it does not work!

I found the cause. Private method _get() only takes one parameter id:

_get(id) {
  return this.Model.findById(id).then(instance => {
    if(!instance) {
      throw new errors.NotFound(`No record found for id '${id}'`);
    }

    return instance;
  })
  .catch(utils.errorHandler);
}

But it must take two parameters id and params.

Support $search

This is a proposed special attribute that allows you to fuzzy match a property. Possibly even multiple properties and/or nested documents.

Suggested syntax:

name: {
  $search: ['alice', 'Alice', 'bo', /$bob/i]
}

Following similar syntax to our other special query filters, this would allow you to filter by a singular value, multiple values (treated like an or) and/or regular expressions directly.

Internally sequelize has a few different options. The easiest way to support this syntax would be to use $ilike: { $any: ['alice', 'Alice', 'bo', 'bob']}. It's a little redundant but I'm not sure if sequelize supports passing a regex.

Model.create ignoring field and virtual setters

Model setters are now being ignored on Model create. This error was introduced in commit c375111 as part of the feathers-sequelize 2.0 upgrade. In particular, this line: c375111#diff-1fdf421c05c1140f6d71444ea2b27638R115

Unlike most sequelize methods such as findAll where the raw option causes sequelize to return raw JSON data, this option bizarrely means something completely different for Model.create (see sequelize docs):

options.raw: If set to true, values will ignore field and virtual setters.

Cannot call remove service method when id field name is custom

I'm having some issues with the remove service method.

I've specified a custom id in my model, e.g.,

  app.use('/tags', service({
    id: 'uuid',
    Model: tags
  }));

But when I call the remove method, this is the SQL that runs:

Executing (default): DELETE FROM `tags` WHERE `id` = '9e098ec7-fc13-4478-ab22-68bd4c416637'

And the API returns error message:

ER_BAD_FIELD_ERROR: Unknown column 'id' in 'where clause'

The line looks suspect: https://github.com/feathersjs/feathers-sequelize/blob/master/src/index.js#L143

I think the intended value is this.id, not just id.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.