Code Monkey home page Code Monkey logo

loopback4-sequelize's Introduction

Deprecated!

As of 24th April 2023, loopback4-sequelize is fully deprecated. New changes are expected to land on the official replacement of this package here: @loopback/sequelize and the same is recommended for current and future users.

loopback4-sequelize

LoopBack

This is a loopback4 extension that provides Sequelize's query builder at repository level in any loopback 4 application. It has zero learning curve as it follows similar interface as DefaultCrudRepository. For relational databases, Sequelize is a popular ORM of choice.

For pending features, refer to the Limitations section below.

Installation

To install this extension in your Loopback 4 project, run the following command:

npm install loopback4-sequelize

You'll also need to install the driver for your preferred database:

# One of the following:
npm install --save pg pg-hstore # Postgres
npm install --save mysql2
npm install --save mariadb
npm install --save sqlite3
npm install --save tedious # Microsoft SQL Server
npm install --save oracledb # Oracle Database

Usage

You can watch a video overview of this extension by clicking here.

Both newly developed and existing projects can benefit from the extension by simply changing the parent classes in the target Data Source and Repositories.

Step 1: Configure DataSource

Change the parent class from juggler.DataSource to SequelizeDataSource like below.

// ...
import {SequelizeDataSource} from 'loopback4-sequelize';

// ...
export class PgDataSource
  extends SequelizeDataSource
  implements LifeCycleObserver {
  // ...
}

SequelizeDataSource accepts commonly used config in the same way as loopback did. So in most cases you won't need to change your existing configuration. But if you want to use sequelize specific options pass them in sequelizeOptions like below:

let config = {
  name: 'db',
  connector: 'postgresql',
  sequelizeOptions: {
    username: 'postgres',
    password: 'secret',
    dialectOptions: {
      ssl: {
        rejectUnauthorized: false,
        ca: fs.readFileSync('/path/to/root.crt').toString(),
      },
    },
  },
};

Note: Options provided in sequelizeOptions will take priority over others, For eg. if you have password specified in both config.password and config.password.sequelizeOptions the latter one will be used.

Step 2: Configure Repository

Change the parent class from DefaultCrudRepository to SequelizeCrudRepository like below.

// ...
import {SequelizeCrudRepository} from 'loopback4-sequelize';

export class YourRepository extends SequelizeCrudRepository<
  YourModel,
  typeof YourModel.prototype.id,
  YourModelRelations
> {
  // ...
}

Relations

Supported Loopback Relations

With SequelizeCrudRepository, you can utilize following relations without any additional configuration:

  1. HasMany Relation
  2. BelongsTo Relation
  3. HasOne Relation
  4. HasManyThrough Relation
  5. ReferencesMany Relation

The default relation configuration, generated using the lb4 relation command (i.e. inclusion resolvers in the repository and property decorators in the model), remain unchanged.

INNER JOIN

Check the demo video of using inner joins here: https://youtu.be/ZrUxIk63oRc?t=76

When using SequelizeCrudRepository, the find(), findOne(), and findById() methods accept a new option called required in the include filter. Setting this option to true will result in an inner join query that explicitly requires the specified condition for the child model. If the row does not meet this condition, it will not be fetched and returned.

An example of the filter object might look like this to fetch the books who contains "Art" in their title, which belongs to category "Programming":

{
  "where": {"title": {"like": "%Art%"}},
  "include": [
    {
      "relation": "category",
      "scope": {
        "where": {
          "name": "Programming"
        }
      },
      "required": true // 👈
    }
  ]
}

SQL Transactions

A Sequelize repository can perform operations in a transaction using the beginTransaction() method.

Isolation levels

When you call beginTransaction(), you can optionally specify a transaction isolation level. It support the following isolation levels:

  • Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED (default)
  • Transaction.ISOLATION_LEVELS.READ_COMMITTED
  • Transaction.ISOLATION_LEVELS.REPEATABLE_READ
  • Transaction.ISOLATION_LEVELS.SERIALIZABLE

Options

Following are the supported options:

{
  autocommit?: boolean;
  isolationLevel?: Transaction.ISOLATION_LEVELS;
  type?: Transaction.TYPES;
  deferrable?: string | Deferrable;
  /**
   * Parent transaction.
   */
  transaction?: Transaction | null;
}

Example

// Get repository instances. In a typical application, instances are injected
// via dependency injection using `@repository` decorator.
const userRepo = await app.getRepository(UserRepository);

// Begin a new transaction.
// It's also possible to call `userRepo.dataSource.beginTransaction` instead.
const tx = await userRepo.beginTransaction({
  isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE,
});

try {
  // Then, we do some calls passing this transaction as an option:
  const user = await userRepo.create(
    {
      firstName: 'Jon',
      lastName: 'Doe',
    },
    {transaction: tx},
  );

  await userRepo.updateById(
    user.id,
    {
      firstName: 'John',
    },
    {transaction: tx},
  );

  // If the execution reaches this line, no errors were thrown.
  // We commit the transaction.
  await tx.commit();
} catch (error) {
  // If the execution reaches this line, an error was thrown.
  // We rollback the transaction.
  await tx.rollback();
}

Switching from loopback defaults to sequelize transaction is as simple as this commit in loopback4-sequelize-transaction-example.

Debug strings reference

There are three built-in debug strings available in this extension to aid in debugging. To learn more about how to use them, see this page.

String Description
Datasource
loopback:sequelize:datasource Database Connections logs
loopback:sequelize:queries Logs Executed SQL Queries and Parameters
Repository
loopback:sequelize:modelbuilder Logs Translation of Loopback Models Into Sequelize Supported Definitions. Helpful When Debugging Datatype Issues

Limitations

Please note, the current implementation does not support the following:

  1. Loopback Migrations (via default migrate.ts). Though you're good if using external packages like db-migrate.

Community contribution is welcome.

Feedback

If you've noticed a bug or have a question or have a feature request, search the issue tracker to see if someone else in the community has already created a ticket. If not, go ahead and make one! All feature requests are welcome. Implementation time may vary. Feel free to contribute the same, if you can. If you think this extension is useful, please star it. Appreciation really helps in keeping this project alive.

Contributing

Please read CONTRIBUTING.md for details on the process for submitting pull requests to us.

Code of conduct

Code of conduct guidelines here.

License

MIT

loopback4-sequelize's People

Contributors

gautam23-sf avatar raghavarorasf avatar semantic-release-bot avatar sfdevops avatar shubhamp-sf avatar tyagi-sunny avatar yeshamavani avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

shubhamp-sf

loopback4-sequelize's Issues

Support calculated fields for Loopback entities

Is your feature request related to a problem? Please describe.

It would be great to add support for the Virtual data type for calculated fields:
https://sequelize.org/docs/v6/core-concepts/getters-setters-virtuals/#virtual-fields

This would also provide a big improvement over the limited support and workarounds for calculated fields when using the Loopback juggler ORM: loopbackio/loopback-next#2707

Describe the solution you'd like
Ability to configure the virtual data type for Loopback entities.

Describe alternatives you've considered
A workaround I found is to define a global Sequelize hook and then mutate the attribute definitions in a beforeDefine hook to switch them to the Sequelize virtual data type.

loopback4-sequelize version update

Describe the bug
Update loopback4-sequelize versions to latest.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Additional context
Add any other context about the problem here.

accept connection pooling, ssl and url string option in datasource

Is your feature request related to a problem? Please describe.

SequelizeDatasource should accept connection pooling, ssl and url string options in datasource

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Usage with a case-sensitive database leads to query “table not found” errors

Great project! Almost a seamless replacement for the loopback juggler. I wasn’t sure if I should post issues here or in the loopback-next repository given the deprecation notice.

Describe the bug
I came across an edge case. The lower cased table name here leads to a “breaking change” for projects relying on the table name matching the casing of the entity definition:

tableName: entityClass.modelName.toLowerCase(),

To Reproduce
Steps to reproduce the behavior:

  1. Set up a MySQL database with case-sensitive table names (https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_lower_case_table_names)
  2. Create a new entity (eg “class Book”)
  3. Use a repository operation (eg find)
  4. Query errors with table “book” not found.

Expected behavior
Successful query using “Book” as the table name.

model property option `hidden` is ignored

Description of the bug
Loopback supports hidden properties that is used when user don't want then in response bodies. While using the SequelizeRepository this behavior is ignored.

Expected behavior
SequelizeRepository's methods should not return any of the hidden properties specified in the response body. Regardless of the fields filter option.

Additional context
There are two ways user can specify hidden properties.

  1. Model settings hiddenProperties
@model({
  settings: {
    hiddenProperties: ['password']
  }
})
  1. At property level
// ...
@property({type: 'string', hidden: true})
// ...

Deprecate this package in favour of `@loopback/sequelize`

Is your feature request related to a problem? Please describe.
Since all of the necessary features are now being provided by the official package @loopback/sequelize

This package can safely be marked as deprecated.

Describe the solution you'd like

  • Add deprecation notice in README file.
  • Run following deprecation command. (to be run by @samarpan-b sir's npm account.)
  • npm deprecate loopback4-sequelize "<message>"
    Replace message with <message> "This package has been deprecated in favour of @loopback/sequelize."

Describe alternatives you've considered
__

Additional context
__

@belongsTo with a default “keyTo” leads to a broken relation query

Describe the bug
The belongsTo relation mapping from Loopback 4 to Sequelize has an edge case when using the default “keyTo” option.

References:
https://loopback.io/doc/en/lb4/BelongsTo-relation.html#relation-metadata

To Reproduce
Steps to reproduce the behavior:

  1. Create 2 Loopback entities (eg Book and Reader)
  2. Create a belongsTo relation in one of them such as:
@belongsTo(() => Reader, null, {
    type: 'string',
    required: false,
    length: 500
  })
  public readerId: string;
  1. Set the 2nd decorator argument to “null” or undefined
  2. Try to query the relation
  3. Observe a broken query to the relation (when using the DEBUG env variable) containing something like “readerreaderId”.

Expected behavior
Successful query when using the default options generated by Loopback for a belongsTo relation.

Additional context
I haven’t confirmed it but I assume the code in this section might need an update:

sourceModel.belongsTo(targetModel, {

Quick workaround is to explicitly set the “{ keyTo }” in the decorator options.

Provision for query logs

Is your feature request related to a problem? Please describe.
There are many cases where we want to see queries which are actually getting executed when there is any database operation.
In loopback juggler we can use DEBUG=loopback:connector:postgresql

It would be great of there is any provision for doing so.

Loopback4-sequelize should support transactions

As our current applications have to support n number of related operations. Either all those operations should commit and succeed or should be rolled back.
Juggler support transactions. Same way would like loopback4-sequelize should support the same.

Support postgres datatype "jsonb"

Is your feature request related to a problem? Please describe.

Current version doesn't support jsonb type which is available in postgres.

Describe the solution you'd like
Currently model properties can specify this as follows

@property({ 
  postgresql: { dataType: 'jsonb' },
})
//...

It should support it the same way.

Describe alternatives you've considered

The currently supported datatype JSON can be used but it's better to to use JSONB when using postgres to utilize the full possibilities of the driver.
See the comparison here: https://www.dbvis.com/thetable/json-vs-jsonb-in-postgresql-a-complete-comparison

Additional context
Reported here: loopbackio/loopback-next#9345 (comment)

Generate a detailed changelog

Is your feature request related to a problem? Please describe.
Right now we dont have well defined changelog

Describe the solution you'd like
A changelog where we have issue description, commit title, details about the commit , the commiter etc

Custom Query Execution Support

Is your feature request related to a problem? Please describe.
Loopback's DefaultCrudRepository support custom sql queries for connectors that supports it. Similar should be enabled for SequelizeCrudRepository.

Describe the solution you'd like
Interface similar to execute function in DefaultCrudRepository can be provided.

Describe alternatives you've considered
Users can currently do this by accessing the repository.dataSource.sequelize.query() but that has a different params and result type. So someone who just need to replace juggler with sequelize shouldn't be asked to modify this code.

Additional context
__

OR WHERE ASSOCIATED

I really confuse for filtering data when i want to make conditions.
My table primary is Events and Inner Join to HomeClubs ands AwaysClubs;

i'm trying to make conditions

Where Events.date = 2024-03-04 AND (Homeclubs.name LIKE 'MANCHESTER' OR AwayClub.name LIKE 'MANCHESTER')

it will be nice if i can know the filter conditions.

Regression in Entity "hiddenProperty" behavior when creating a new entity via SequelizeCrudRepository#create

Describe the bug
I noticed that Entity properties marked as hidden are not accessible immediately after creation using a repository extending the SequelizeCrudRepository.

I traced it to this code:

To Reproduce
Steps to reproduce the behavior:

  1. Create a new entity with a hidden field (e.g. Book with a decorator like "@model( { hiddenProperties: [ 'theEnding' ] } )") using Repository.create()
  2. Try to access the hidden property in the backend code
  3. Error: "Property is undefined"

Expected behavior
Hidden properties should be excluded from the REST API responses but still be accessible in the backend.

Additional context
It seems to be related to this PR: 3e254fd

My understanding is that Loopback will call "toJSON" when serializing the response which should already handle excluding the hidden properties: https://github.com/loopbackio/loopback-next/blob/057150d78908045c3ac2405b0ba2aac65eb17416/packages/repository/src/model.ts#L341-L370

It seems like changing this line:

return new this.entityClass(this.excludeHiddenProps(data.toJSON())) as T;

to the following might be all it takes:

return new this.entityClass(data.toJSON()) as T;

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.