Skip to content

Commit

Permalink
docs: dynamic datasource, model, repository, and controller
Browse files Browse the repository at this point in the history
Describe how to create datasources, models, repositories, and
controllers at runtime.
  • Loading branch information
Yaapa Hage authored and hacksparrow committed Jun 5, 2020
1 parent 3304963 commit 2ec9bdf
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 0 deletions.
50 changes: 50 additions & 0 deletions docs/site/Controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,25 @@ export class HelloController {
- `@param.query.number` specifies in the spec being generated that the route
takes a parameter via query which will be a number.

## Class factory to allow parameterized decorations

Since decorations applied on a top-level class cannot have references to
variables, you can create a class factory that allows parameterized decorations
as shown in the example below.

```ts
function createControllerClass(version: string, basePath: string) {
@api({basePath: `${basePath}`})
class Controller {
@get(`/${version}`) find() {}
}
}
```

For a complete example, see
[parameterized-decoration.ts](https://github.com/strongloop/loopback-next/blob/master/examples/context/src/parameterized-decoration.ts)
.

## Handling Errors in Controllers

In order to specify errors for controller methods to throw, the class
Expand Down Expand Up @@ -269,3 +288,34 @@ export class HelloController {
}
}
```

## Creating Controllers at Runtime

A controller can be created for a model at runtime using the
`defineCrudRestController` helper function from the `@loopback/rest-crud`
package. It accepts a Model class and a `CrudRestControllerOptions` object.
Dependency injection for the controller has to be configured by applying the
`inject` decorator manually as shown in the example below.

```ts
const basePath = '/' + bookDef.name;
const BookController = defineCrudRestController(BookModel, {basePath});
inject(repoBinding.key)(BookController, undefined, 0);
```

The controller is then attached to the app by calling the `app.controller()`
method.

```ts
app.controller(BookController);
```

The new CRUD REST endpoints for the model will be available on the app now.

If you want a customized controller, you can create a copy of
`defineCrudRestController`'s
[implementation](https://github.com/strongloop/loopback-next/blob/00917f5a06ea8a51e1f452f228a6b0b7314809be/packages/rest-crud/src/crud-rest.controller.ts#L129-L269)
and modify it according to your requirements.

For details about `defineCrudRestController` and `CrudRestControllerOptions`,
refer to the [@loopback/rest-crud API documentation](./apidocs/rest-crud.html).
29 changes: 29 additions & 0 deletions docs/site/DataSources.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,32 @@ export class DbDataSource extends juggler.DataSource {
}
}
```

### Creating a DataSource at Runtime

A datasource can be created at runtime by creating an instance of
`juggler.DataSource`. It requires a name for the datasource, the connector, and
the connection details.

```ts
import {juggler} from '@loopback/repository';
const dsName = 'bookstore-ds';
const bookDs = new juggler.DataSource({
name: dsName,
connector: require('loopback-connector-mongodb'),
url: 'mongodb://sysop:moon@localhost',
});
await bookDs.connect();
app.dataSource(bookDs, dsName);
```

For details about datasource options, refer to the [DataSource
documentation])(https://apidocs.strongloop.com/loopback-datasource-juggler/#datasource)
.

Attach the newly created datasource to the app by calling `app.dataSource()`.

{% include note.html content="
The `app.datasource()` method is available only on application classes
with `RepositoryMixin` applied.
" %}
46 changes: 46 additions & 0 deletions docs/site/Model.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,52 @@ export class Customer {
}
```

## Defining a Model at runtime

Models can be created at runtime using the `defineModelClass()` helper function
from the `@loopback/repository` class. It expects a base model to extend
(typically `Model` or `Entity`), followed by a `ModelDefinition` object as shown
in the example below.

```ts
const bookDef = new ModelDefinition('Book')
.addProperty('id', {type: 'number', id: true})
.addProperty('title', {type: 'string'});
const BookModel = defineModelClass<typeof Entity, {id: number; title?: string}>(
Entity, // Base model
bookDef, // ModelDefinition
);
```

You will notice that we are specifying generic parameters for the
`defineModelClass()` function. The first parameter is the base model, the second
one is an interface providing the TypeScript description for the properties of
the model we are defining. If the interface is not specified, the generated
class will have only members inherited from the base model class, which
typically means no properties.

In case you need to use an existing Model as the base class, specify the Model
as the base class instead of `Entity`.

```ts
// Assuming User is a pre-existing Model class in the app
import {User} from './user.model';
import DynamicModelCtor from '@loopback/repository';
const StudentModel = defineModelClass<
typeof User,
// id being provided by the base class User
{university?: string}
>(User, studentDef);
```

If you want make this new Model available from other parts of the app, you can
call `app.model(StudentModel)` to create a binding for it.

{% include note.html content="
The `app.model()` method is available only on application classes with
`RepositoryMixin` applied.
" %}

## Model Discovery

LoopBack can automatically create model definitions by discovering the schema of
Expand Down
73 changes: 73 additions & 0 deletions docs/site/Repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,79 @@ Please See [Testing Your Application](Testing-your-application.md) section in
order to set up and write unit, acceptance, and integration tests for your
application.

## Creating Repositories at Runtime

Repositories can be created at runtime using the `defineCrudRepositoryClass`
helper function from the `@loopback/rest-crud` package. It creates
`DefaultCrudRepository`-based repository classes by default.

```ts
const BookRepository = defineCrudRepositoryClass<
Book,
typeof Book.prototype.id,
BookRelations
>(BookModel);
```

In case you want to use a non-`DefaultCrudRepository` repository class or you
want to create a custom repository, use the `defineRepositoryClass()` helper
function instead. Pass a second parameter to this function as the base class for
the new repository.

There are two options for doing this:

#### 1. Using a base repository class

Create a base repository with your custom implementation, and then specify this
repository as the base class.

```ts
class MyRepoBase<
E extends Entity,
IdType,
Relations extends object
> extends DefaultCrudRepository<E, IdType, Relations> {
// Custom implementation
}

const BookRepositoryClass = defineRepositoryClass<
typeof BookModel,
MyRepoBase<BookModel, typeof BookModel.prototype.id, BookRelations>
>(BookModel, MyRepoBase);
```

#### 2. Using a Repository mixin

Create a repository mixin with your customization as shown in the
[Defining A Repository Mixin Class Factory Function](https://loopback.io/doc/en/lb4/migration-models-mixins.html#defining-a-repository-mixin-class-factory-function)
example, apply the mixin on the base repository class (e.g.
`DefaultCrudRepository`) then specify this combined repository as the base class
to be used.

```ts
const BookRepositoryClass = defineRepositoryClass<
typeof BookModel,
DefaultCrudRepository<
BookModel,
typeof BookModel.prototype.id,
BookRelations
> &
FindByTitle<BookModel>
>(BookModel, FindByTitleRepositoryMixin(DefaultCrudRepository));
```

Dependency injection has to be configured for the datasource as shown below.

```ts
inject(`datasources.${dsName.name}`)(BookRepository, undefined, 0);
const repoBinding = app.repository(BookRepository);
```

{% include note.html content="
The `app.repository()` method is available only on application classes
with `RepositoryMixin` applied.
" %}

## Access KeyValue Stores

We can now access key-value stores such as [Redis](https://redis.io/) using the
Expand Down

0 comments on commit 2ec9bdf

Please sign in to comment.