diff --git a/docs/site/Model.md b/docs/site/Model.md index f157c24903aa..c6e4b0df3e6f 100644 --- a/docs/site/Model.md +++ b/docs/site/Model.md @@ -139,9 +139,10 @@ PersistedModel classes to enforce `strict` mode. LB4 supports creating a model that allows both well-defined but also arbitrary extra properties for **NoSQL** databases such as MongoDB. You need to disable `strict` mode in model settings. Besides modifying model settings directly, it -can also be done through the CLI by setting `allowed free-from properties` to -true by saying yes when given this prompt and telling TypeScript to allow -arbitrary additional properties to be set on model instances. +can also be done through the CLI by setting +`Allow additional (free-form) properties` to `true` by saying yes when given +this prompt and telling TypeScript to allow arbitrary additional properties to +be set on model instances. ```ts @model({settings: {strict: false}}) @@ -156,7 +157,7 @@ class MyFlexibleModel extends Entity { } ``` -### Model Decorator +## Model Decorator The model decorator can be used without any additional parameters, or can be passed in a ModelDefinitionSyntax: @@ -190,29 +191,15 @@ class Product extends Entity { } ``` -However, the model decorator in LoopBack 4 is not exactly the same as what it is -in LoopBack 3. For example, in -[lb3](https://loopback.io/doc/en/lb3/Model-definition-JSON-file.html) we can -pass in a model definition syntax in the model decorator, such as properties, -options, relation etc. But not all these entries are available in lb4 model -decorator: +{% include note.html content="If you used LoopBack 3 before, the model decorator in LoopBack 4 is not exactly the same as what it is +in LB3. For example, properties and relations cannot be defined through the model decorator. Please check the following section for available entries." %} -NOTICE: in LoopBack 4 we only support `settings` in the ModelDefinitionSyntax -for now. Those `top-level properties` in lb3 now are passed in `settings`. - -- `properties` now are defined in `@property` decorator (see below for more - information). -- [`options`](https://loopback.io/doc/en/lb3/Model-definition-JSON-file.html#options) - in lb3 doesn't have the mapping feature in LB4 yet. (see - [issue #2142](https://github.com/strongloop/loopback-next/issues/2142) for - further discussion.) - As for entries in `settings`, LoopBack 4 supports these built-in entries for now: -#### Supported Entries of Settings +### Supported Entries of Model Definition @@ -382,7 +366,7 @@ To discover more about `Model Decorator` in LoopBack 4, please check and [model-builder file](https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/model-builder.js). -#### Hidden properties +### Hidden Properties The properties are stored in the database, available in JS/TS code, can be set via POST/PUT/PATCH requests, but they are removed from response bodies @@ -407,12 +391,45 @@ class MyUserModel extends Entity { } ``` -### Property Decorator +### Scope + +_Scope_ enables you to set a scope that will apply to every query made by the +model's repository. + +If you wish for a scope to be applied across all queries to the model, set the +scope to do so. For example: + +```ts +@model({ + settings: { + scope: { + limit: 2, + where: {deleted: false} + }, + } +}) +export class Product extends Entity { + ... +``` + +Now, any CRUD operation with a query parameter runs in the default scope will be +applied; for example, assuming the above scope, a find operation such as + +```ts +await ProductRepository.find({offset: 0}); +``` + +Becomes the equivalent of this: + +```ts +await ProductRepository.find({offset: 0, limit: 2, where: {deleted: false}}); +``` + +## Property Decorator -The property decorator takes in the same arguments used in LoopBack 3 for -individual property entries: +LoopBack 4 uses the property decorator for property definitions. ```ts @model() @@ -432,78 +449,277 @@ class Product extends Entity { } ``` -The complete list of valid attributes for property definitions can be found in -LoopBack 3's -[Model definition section](https://loopback.io/doc/en/lb3/Model-definition-JSON-file.html#properties). +Here are general attributes for property definitions: -You can also specify the validation rules in the field `jsonSchema`. For -example: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyRequired?TypeDescription
defaultNoAny* + Default value for the property. The type must match that specified by type. NOTE: if you have both default value set and required set to true, you will still need to include the property in the request body of POST/PUT requests as LoopBack follows the OpenAPI standard that required means the user needs to provide the field in the request always. +
defaultFnNoString + A name of the function to call to set the default value for the property. Must be one of: +
    +
  • + "guid": generate a new globally unique identifier (GUID) using the computer MAC address and current time (UUID version 1). +
  • +
  • "uuid": generate a new universally unique identifier (UUID) using the computer MAC address and current time (UUID version 1).
  • +
  • "uuidv4": generate a new universally unique identifier using the UUID version 4 algorithm.
  • +
  • "now": use the current date and time as returned by new Date()
  • +
+ NOTE: the value of defaultFn is generated by LoopBack itself. If you'd like to use database built-in uuid functions (MySQL or Postgres for example), please check the README file of the corresponding connector. +
descriptionNoString or Array + Documentation for the property. + You can split long descriptions into arrays of strings (lines) to keep line lengths manageable. For example: +
[
+"LoopBack 4 is a highly extensible Node.js and TypeScript framework",
+"for building APIs and microservices.",
+"Follow us on GitHub: https://github.com/strongloop/loopback-next."
+]
+
docNoStringDocumentation for the property. Deprecated, use "description" instead.
idNoBoolean + Whether the property is a unique identifier. Default is false. + See ID properties section below for detailed explanations. +
indexNoBooleanWhether the property represents a column (field) that is a database index.
requiredNoBoolean + Whether a value for the property is required in the request body for creating or updating a model instance.

+ Default is false. NOTE: As LoopBack follows the OpenAPI standard, required means the user needs to provide the field in the request always. POST/PUT requests might get rejected if the request body doesn't include the required property even it has default value set. +
typeYesString + Property type. Can be any type described in LoopBack types. +
+ +### ID Properties + +LoopBack 4 expects a model to have one _ID property_ that uniquely identifies +the model instance. + +{% include important.html content="LB4 doesn't support composite keys for now, e.g joining two tables with more than one source key. Related GitHub issue: [Composite primary/foreign keys](https://github.com/strongloop/loopback-next/issues/1830)" %} + +To explicitly specify a property as ID, set the `id` property of the option +to `true`. The `id` property value must be one of: + +- `true`: the property is an ID. +- `false` (or any value that converts to false): the property is not an ID + (default). + +In database terms, the ID property is primary key column. Such properties are +defined with the 'id' attribute set to true. + +For example, ```ts -@model() -class Product extends Entity { @property({ - name: 'name', - description: "The product's common name.", - type: 'string', - // Specify the JSON validation rules here - jsonSchema: { - maxLength: 30, - minLength: 10, - errorMessage: - 'name must be at least 10 characters and maximum 30 characters', - }, + type: 'number', + id: true, }) - public name: string; -} + id: number; ``` -Check out the documentation of -[Parsing requests](Parsing-requests.md#request-body) to see how to do it in -details. +In LoopBack, [auto-migration](Database-migrations.md) helps the user create +relational database schemas based on definitions of their models. Here are some +id property settings that can be used for auto-migration / auto-update: - + + + + + + + + + + + + + + + + + + + + + + + +
KeyRequired?TypeDescription
generatedNoBooleanFor auto-migrate usage. The generated property indicates the ID will be automatically generated by the database. When it is set to true, the value of the id property will be generated by the database automatically with its default type (e.g integer for MySQL and string for MongoDB).
useDefaultIdTypeNoBooleanFor auto-migrate usage. Set it to false when it's needed to auto-generate non-default type property values. For example, for PostgreSQL, to use uuid as the id property, the id type should set to string, generated should set to true, and this field should be set to false. Please check each connector's README file for more information about auto-migration/auto-update.
-The property decorator leverages LoopBack's -[metadata package](https://github.com/strongloop/loopback-next/tree/master/packages/metadata) -to determine the type of a particular property. +Tips: -{% include note.html content=" Currently, property types must be specified -explicitly either on the property itself or via the `type` option of the -property decorator. Aliased types or types that extracted from a class or -interface (e.g. `public name: OtherClass['otherProperty']`) will not work -properly and will result in the property type being resolved as an empty object -rather than the intended type in the generated OpenAPI specifcation. This is due -to a limitation and flaw in the way TypeScript currently generates the metadata -that is used to generate the OpenAPI specification for the application. +1. LoopBack CRUD methods expect the model to have an "id" property if the model + is backed by a database. +2. A model without any "id" properties can only be used without attaching to a + database. +3. If an ID property has `generated` set to `true`, the connector decides what + type to use for the auto-generated key. For example for SQL Server, it + defaults to `number`. This can be overwritten by setting `useDefaultIdType` + to `false`. +4. Check out [Database Migration](Database-migrations.md) if you'd like to have + LoopBack 4 create relational database's schemas for you based on model + definitions. Always check [Database Connectors](Database-connectors.md) for + details and examples for database migration / discover. -Example: +### Data Mapping Properties -```ts -export class StandardUser { - public email: string; - public anotherProperty: boolean; -} +When using a relational database data source, you can specify the following +properties that describe the columns in the database. -@model() -export class UserModel { - @property() - public email: StandardUser['email']; // => results in \"__metadata(\"design:type\", Object)\" instead of \"__metadata(\"design:type\", String)\" -} -``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
columnNameStringColumn name
dataTypeStringData type as defined in the database
dataLengthNumberData length
dataPrecisionNumberNumeric data precision
dataScaleNumberNumeric data scale
nullableBooleanIf true, data can be null
-(see [Issue #3863](https://github.com/strongloop/loopback-next/issues/3863) for -more details) " %} +For example, to map a property to a column in an Oracle database table, use the +following: ```ts -@model() -class Product extends Entity { - @property() - public name: string; // The type information for this property is String. -} +... + @property({ + type: 'number', + required: false, + scale: 0, + id: true, + postgresql: { + columnName: 'testId', + dataType: 'bigint', + dataLength: null, + dataPrecision: null, + dataScale: 0, + nullable: 'NO', + }, + }) + id: number; +... ``` +Notice that with the mapping, the model property name (`id`) and the +corresponding database column name (`testId`) can be different if needed. + +
Non-public Information
+ Removed until https://github.com/strongloop/loopback-datasource-juggler/issues/128 is resolved. +

Conversion and formatting properties

+

Format conversions are declared in properties, as described in the following table:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeDescription
trimBooleanWhether to trim the string
lowercaseBooleanWhether to convert a string to lowercase
uppercaseBooleanWhether to convert a string to uppercase
formatRegular expressionFormat for a date property.
+
+ ### Array Property Decorator There is a limitation to the metadata that can be automatically inferred by @@ -580,7 +796,77 @@ class TestModel { } ``` -### JSON Schema inference +### Validation Rules + +You can also specify the validation rules in the field `jsonSchema`. For +example: + +```ts +@model() +class Product extends Entity { + @property({ + name: 'name', + description: "The product's common name.", + type: 'string', + // Specify the JSON validation rules here + jsonSchema: { + maxLength: 30, + minLength: 10, + errorMessage: + 'name must be at least 10 characters and maximum 30 characters', + }, + }) + public name: string; +} +``` + +Check out the documentation of +[Parsing requests](Parsing-requests.md#request-body) to see how to do it in +details. + + + +The property decorator leverages LoopBack's +[metadata package](https://github.com/strongloop/loopback-next/tree/master/packages/metadata) +to determine the type of a particular property. + +{% include note.html content=" Currently, property types must be specified +explicitly either on the property itself or via the `type` option of the +property decorator. Aliased types or types that extracted from a class or +interface (e.g. `public name: OtherClass['otherProperty']`) will not work +properly and will result in the property type being resolved as an empty object +rather than the intended type in the generated OpenAPI specifcation. This is due +to a limitation and flaw in the way TypeScript currently generates the metadata +that is used to generate the OpenAPI specification for the application. + +Example: + +```ts +export class StandardUser { + public email: string; + public anotherProperty: boolean; +} + +@model() +export class UserModel { + @property() + public email: StandardUser['email']; // => results in \"__metadata(\"design:type\", Object)\" instead of \"__metadata(\"design:type\", String)\" +} +``` + +(see [Issue #3863](https://github.com/strongloop/loopback-next/issues/3863) for +more details) " %} + +```ts +@model() +class Product extends Entity { + @property() + public name: string; // The type information for this property is String. +} +``` + +## JSON Schema Inference Use the `@loopback/repository-json-schema module` to build a JSON schema from a decorated model. Type information is inferred from the `@model` and `@property` @@ -645,7 +931,7 @@ allows for complex and nested custom type definition building. The example above illustrates this point by having the custom type `Category` as a property of our `Product` model definition. -#### Supported JSON keywords +### Supported JSON Keywords {% include note.html content=" This feature is still a work in progress and is incomplete. @@ -672,3 +958,7 @@ However, this also means that the provided schema decorators will serve no purpose for these ORMs/ODMs. Some of these frameworks may also provide decorators with conflicting names (e.g. another `@model` decorator), which might warrant avoiding the provided juggler decorators. + +## FAQ + +Feel overwhelmed? Here are some examples of setting up models: