Overview

A model describes business domain objects, for example, Customer, Address,and Order. It usually defines a list of properties with name, type, and otherconstraints.

Note:Models describe the shape of data. Behavior like CRUD operations is added by repositories. This is different from LoopBack 3.x where models implement behavior too.

Tip: A single model can be used with multiple different Repositories.

Models can be used for data exchange on the wire or between different systems.For example, a JSON object conforming to the Customer model definition can bepassed in REST/HTTP payload to create a new customer or stored in a documentdatabase such as MongoDB. Model definitions can also be mapped to other forms,such as relational database schemas, XML schemas, JSON schemas, OpenAPI schemas,or gRPC message definitions, and vice versa.

There are two subtly different types of models for domain objects:

  • Value Object: A domain object that does not have an identity (ID). Itsequality is based on the structural value. For example, Address can bemodeled as a Value Object because two US addresses are equal if they havethe same street number, street name, city, and zip code values. For example:
  1. {
  2. "name": "Address",
  3. "properties": {
  4. "streetNum": "string",
  5. "streetName": "string",
  6. "city": "string",
  7. "zipCode": "string"
  8. }
  9. }
  • Entity: A domain object that has an identity (ID). Its equality is based onthe identity. For example, Customer can be modeled as an Entity becauseeach customer has a unique customer id. Two instances of Customer with thesame customer id are equal since they refer to the same customer. For example:
  1. {
  2. "name": "Customer",
  3. "properties": {
  4. "id": "string",
  5. "lastName": "string",
  6. "firstName": "string",
  7. "email": "string",
  8. "address": "Address"
  9. }
  10. }

Currently, we provide the @loopback/repository module, which provides specialdecorators for adding metadata to your TypeScript/JavaScript classes in order touse them with the legacy implementation of thedatasource juggler.

Definition of a Model

At its core, a model in LoopBack is a simple JavaScript class.

  1. export class Customer {
  2. email: string;
  3. isMember: boolean;
  4. cart: ShoppingCart;
  5. }

Extensibility is a core feature of LoopBack. There are external packages thatadd additional features, for example, integration with the juggler bridge orJSON Schema generation. These features become available to a LoopBack modelthrough the @model and @property decorators from the @loopback/repositorymodule.

  1. import {model, property} from '@loopback/repository';
  2. @model()
  3. export class Customer {
  4. @property()
  5. email: string;
  6. @property()
  7. isMember: boolean;
  8. @property()
  9. cart: ShoppingCart;
  10. }

Model Discovery

LoopBack can automatically create model definitions by discovering the schema ofyour database. See Discovering models for more detailsand a list of connectors supporting model discovery.

Using the Juggler Bridge

To define a model for use with the juggler bridge, extend your classes fromEntity and decorate them with the @model and @property decorators.

  1. import {model, property, Entity} from '@loopback/repository';
  2. @model()
  3. export class Product extends Entity {
  4. @property({
  5. id: true,
  6. description: 'The unique identifier for a product',
  7. })
  8. id: number;
  9. @property()
  10. name: string;
  11. @property()
  12. slug: string;
  13. constructor(data?: Partial<Product>) {
  14. super(data);
  15. }
  16. }

Models are defined primarily by their TypeScript class. By default, classesforbid additional properties that are not specified in the type definition. Thepersistence layer respects this constraint and configures underlyingPersistedModel classes to enforce strict mode.

To create a model that allows both well-defined but also arbitrary extraproperties, you need to disable strict mode in model settings through the CLIand tell TypeScript to allow arbitrary additional properties to be set on modelinstances.

  1. @model({settings: {strict: false}})
  2. class MyFlexibleModel extends Entity {
  3. @property({id: true})
  4. id: number;
  5. // Define well-known properties here
  6. // Add an indexer property to allow additional data
  7. [prop: string]: any;
  8. }

Model Decorator

The model decorator can be used without any additional parameters, or can bepassed in a ModelDefinitionSyntax:

  1. @model({
  2. name: 'Category',
  3. settings: {
  4. // etc...
  5. },
  6. // define properties by @property decorator below
  7. })
  8. class Category extends Entity {
  9. // etc...
  10. @property({type: 'number'})
  11. categoryId: number;
  12. }

The model decorator already knows the name of your model class, so you can omitit.

  1. @model()
  2. class Product extends Entity {
  3. name: string;
  4. // other properties...
  5. }

However, the model decorator in LoopBack 4 is not exactly the same as what it isin LoopBack 3. For example, inlb3 we canpass in a model definition syntax in the model decorator, such as properties,options, relation etc. But not all these entries are available in lb4 modeldecorator:NOTICE: in LoopBack 4 we only support settings in the ModelDefinitionSyntaxfor now. Those top-level properties in lb3 now are passed in settings.

  • properties now are defined in @property decorator (see below for moreinformation).
  • optionsin lb3 doesn’t have the mapping feature in LB4 yet. (seeissue #2142 forfurther discussion.)As for entries in settings, LoopBack 4 supports these built-in entries fornow:

Supported Entries of Settings

PropertyTypeDefaultDescription
descriptionStringNone Optional description of the model. We only support string type for now. (see issue #3428 for more discussion.)
forceIdBooleantrue If true, prevents clients from setting the auto-generated ID value manually.
strictBoolean or Stringtrue. In LB4, the default for this entry is set to be true. Specifies whether the model accepts only predefined properties or not. One of:- true: Only properties defined in the model are accepted. Use if you want to ensure the model accepts only predefined properties. If you try to save a model instance with properties that are not predefined, LoopBack throws a ValidationError.- false: The model is an open model and accepts all properties, including ones not predefined in the model. This mode is useful to store free-form JSON data to a schema-less database such as MongoDB.- "filter": Only properties defined in the model are accepted. If you load or save a model instance with properties that are not predefined, LoopBack will ignore them. This is particularly useful when dealing with old data that you wish to lose without a migration script.
idInjectionBooleantrue Whether to automatically add an id property to the model:- true: id property is added to the model automatically. This is the default.- false: id property is not added to the model See ID properties for more information. The idInjection property in options (if present) takes precedence.
nameStringNoneName of the model.
scopesObjectN/ASee Scopes in lb3 docs.

Unsupported Entries of Settings

PropertyDescription
acls (TBD)
baseThis entry is no longer being used. This is done by the typical Js/Tsc classes inheritance way in LB4:
  1. @model() class MySuperModel extends MyBaseModel {…}
excludeBaseProperties(TBD)
http This entry affects HTTP configuration in LB3. Since in LB4 http configurations are inferred from controller members and the rest server, this field is not applicable.
options (TBD) see issue #2142 for further discussion.
pluralThis entry is part of HTTP configuration in LB3. So it's not applicable for the same reason as http above.
propertiesThis entry is no longer being used as we introduced @property decorator in LB4. See @property decorator below to discover moer about how to define properties for your models.
relations With the introduction of repositories, now relations are defined by relations decorators in LB4. See Relations for more details.
remoting.normalizeHttpPath This entry is part of HTTP configuration in LB3. So it's not applicable for the same reason as http above.
replaceOnPUTThis entry is no longer supported as LB4 controllers scaffolded by LB4 controller, PUT is always calling replaceById. Users are free to change the generated code to call patchById if needed.

To discover more about Model Decorator in LoopBack 4, please checklegacy-juggler-bridge fileandmodel-builder file.

Hidden properties

The properties are stored in the database, available in JS/TS code, can be setvia POST/PUT/PATCH requests, but they are removed from response bodies(.toJSON() output).

To hide a property, you can use the hiddenProperties setting like this:

  1. @model({
  2. settings: {
  3. hiddenProperties: ['password']
  4. }
  5. })
  6. class MyUserModel extends Entity {
  7. @property({id: true})
  8. id: number;
  9. @property({type: 'string'})
  10. email: string;
  11. @property({type: 'string'})
  12. password: string;
  13. ...
  14. }

Property Decorator

The property decorator takes in the same arguments used in LoopBack 3 forindividual property entries:

  1. @model()
  2. class Product extends Entity {
  3. @property({
  4. name: 'name',
  5. description: "The product's common name.",
  6. type: 'string',
  7. })
  8. public name: string;
  9. @property({
  10. type: 'number',
  11. id: true,
  12. })
  13. id: number;
  14. }

The complete list of valid attributes for property definitions can be found inLoopBack 3’sModel definition section.

You can also specify the validation rules in the field jsonSchema. Forexample:

  1. @model()
  2. class Product extends Entity {
  3. @property({
  4. name: 'name',
  5. description: "The product's common name.",
  6. type: 'string',
  7. // Specify the JSON validation rules here
  8. jsonSchema: {
  9. maxLength: 30,
  10. minLength: 10,
  11. },
  12. })
  13. public name: string;
  14. }

Check out the documentation ofParsing requests to see how to do it indetails.The property decorator leverages LoopBack’smetadata packageto determine the type of a particular property.

  1. @model()
  2. class Product extends Entity {
  3. @property()
  4. public name: string; // The type information for this property is String.
  5. }

Array Property Decorator

There is a limitation to the metadata that can be automatically inferred byLoopBack, due to the nature of arrays in JavaScript. In JavaScript, arrays donot possess any information about the types of their members. By traversing anarray, you can inspect the members of an array to determine if they are of aprimitive type (string, number, array, boolean), object or function, but thisdoes not tell you anything about what the value would be if it were an object orfunction.

For consistency, we require the use of the @property.array decorator, whichadds the appropriate metadata for type inference of your array properties.

  1. @model()
  2. class Order extends Entity {
  3. @property.array(Product)
  4. items: Product[];
  5. }
  6. @model()
  7. class Thread extends Entity {
  8. // Note that we still require it, even for primitive types!
  9. @property.array(String)
  10. posts: string[];
  11. }

Additionally, the @property.array decorator can still take an optional secondparameter to define or override metadata in the same fashion as the @propertydecorator.

  1. @model()
  2. class Customer extends Entity {
  3. @property.array(String, {
  4. name: 'names',
  5. required: true,
  6. })
  7. aliases: string[];
  8. }

JSON Schema inference

Use the @loopback/repository-json-schema module to build a JSON schema from adecorated model. Type information is inferred from the @model and @propertydecorators. The @loopback/repository-json-schema module contains thegetJsonSchema function to access the metadata stored by the decorators tobuild a matching JSON schema of your model.

  1. import {model, property} from '@loopback/repository';
  2. import {getJsonSchema} from '@loopback/repository-json-schema';
  3. @model()
  4. class Category {
  5. @property()
  6. name: string;
  7. }
  8. @model()
  9. class Product {
  10. @property({required: true})
  11. name: string;
  12. @property()
  13. type: Category;
  14. }
  15. const jsonSchema = getJsonSchema(Product);

jsonSchema from above would return:

  1. {
  2. "title": "Product",
  3. "properties": {
  4. "name": {
  5. "type": "string"
  6. },
  7. "type": {
  8. "$ref": "#/definitions/Category"
  9. }
  10. },
  11. "definitions": {
  12. "Category": {
  13. "properties": {
  14. "name": {
  15. "type": "string"
  16. }
  17. }
  18. }
  19. },
  20. "required": ["name"]
  21. }

If a custom type is specified for a decorated property in a model definition,then a reference$reffield is created for it and a definitions sub-schema is created at thetop-level of the schema. The definitions sub-schema is populated with the typedefinition by recursively calling getJsonSchema to build its properties. Thisallows for complex and nested custom type definition building. The example aboveillustrates this point by having the custom type Category as a property of ourProduct model definition.

Supported JSON keywords

Note:

This feature is still a work in progress and is incomplete.

Following are the supported keywords that can be explicitly passed into thedecorators to better tailor towards the JSON Schema being produced:

KeywordsDecoratorTypeDefaultDescription
title@modelstringmodel nameName of the model
description@modelstring Description of the model
array@propertyboolean Used to specify whether the property is an array or not
required@propertyboolean Used to specify whether the property is required or not

Other ORMs

You might decide to use an alternative ORM/ODM in your LoopBack application.LoopBack 4 no longer expects you to provide your data in its own custom Modelformat for routing purposes, which means you are free to alter your classes tosuit these ORMs/ODMs.

However, this also means that the provided schema decorators will serve nopurpose for these ORMs/ODMs. Some of these frameworks may also providedecorators with conflicting names (e.g. another @model decorator), which mightwarrant avoiding the provided juggler decorators.