-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
From model definition to REST API with no custom repository/controller classes #2036
Comments
Why generate a model first? I think it's better if we can put an OpenAPI YAML or JSON file on some folder, and from the definition, generate the model, controller, and repository. |
Sure, that's an option too! However, if the model is defined in a YAML/JSON file, then there are no type definitions available and as a result, the compiler cannot check types in code using the generated model & repository - typically acceptance/integration tests using JS/TS API to setup and clean the database between the tests. |
@gczobel-f5 We already have To allow a set of OpenAPI specs in a folder to be discovered, we probably need to do the following:
|
@raymondfeng In my mind, I see this feature generating the model/controller/repository for simple CRUD operations in memory only, without creating any file. |
Cross-posting #1889 (comment)
Yes please! It's our design goal to keep LoopBack 4+ extensible and allow the community to experiment with different styles of project layout. We don't have bandwidth to implement and maintain all such extensions ourselves, but we are happy to help anybody willing to step up and write such extension themselves. As for repositories, it's already possible to use In #740, @raymondfeng has proposed an extension providing REST API via a base controller class With these two buildings blocks, I imagine one can write a small piece of code accepting a model class an exposing it via REST API, see the mock-up implementation below. It's just an illustration to show what I mean, it will probably not work out of the box and may require cleanup. // usage - this can be automated via a custom Booter
import {Product, Category} from './models';
export class MyApplication extends RepositoryMixin(RestApplication) {
constructor() {
super();
exposeModelViaRest(app, Product);
exposeModelViaRest(app, Category;
}
}
// implementation
function exposeModelViaRest<T extends Entity>(
app: ApplicationWithRepositories,
dataSourceName: string,
modelCtor: Class<T>
) {
const REPOSITORY_KEY = `repositories.${modelCtor.modelName}Repository`;
app.bind(REPOSITORY_KEY).toDynamicValue(() => {
const ds = await ctx.get(`datasources.${dataSourceName}`);
return new DefaultCrudRepository(modelCtor, ds);
});
app.controller(createCrudControllerForModel(modelCtor, REPOSITORY_KEY));
}
function createCrudControllerForModel<T extends Entity>(
modelCtor: class<T>,
repositoryKey: string
) {
// It is not possible to access closure variables like "BaseController"
// from a dynamically defined function. The solution is to
// create a dynamically defined factory function that accepts
// closure variables as arguments.
const name = modelCtor.modelName + 'Controller';
const factory = new Function('BaseController', 'repositoryName', `
class ${name} extends BaseController {
constructor(repository) {
super(repository);
}
}`);
const controllerCtor = factory(CrudController, repositoryName);
// apply @inject() decorator on the first constructor parameter
inject(repositoryKey)(controllerCtor, undefined, 0);
return controllerCtor;
} |
@bajtos , we need some clarification please. The title mentions the need for "no repository or controller" classes, yet controller classes are mention a few times.
|
@bajtos, could you please confirm? Thanks. |
Moved this Epic from Q2 to Q3 because most of the stories (see my previous comment) are already assigned to Q3. |
The tasks we've targeted for 2019Q3 is done. Moving this epic to 2019Q4 for the remaining items. |
Blog post is ready to publish; closing this EPIC as done 👍 |
In LoopBack 3, it was very easy to get a fully-featured CRUD REST API with very little code: a model definition describing model properties + a model configuration specifying which datasource to use.
Let's provide the same simplicity to LB4 users too.
@loopback/boot
processes this configuration and registers appropriate repositories & controllers with the app.For example, to create a
Product
model, attach it to adb
datasources and expose a CRUD REST API, all that the developer needs is to add the following two files to their project (plus any npm packages that implement this functionality):models/product.ts
:public-models/product.json
:Note: having config for multiple models in a single JSON file, as we do in LB 3.x, can quickly become a maintenance nightmare - see strongloop/loopback#1316. It will be better to have one config file per model.
An example file for a model using KeyValue pattern (not CRUD) -
public-models/cache-entry.json
:See also
Include related models in "GET /mymodels" results #1889:
/cc @GuillaumeJasmin
Declarative Support #565 Declarative Support
Acceptance criteria
A new extension (npm package living in loopback-next monorepo) providing CrudController class. See [WIP] [RFC] feat: Add REST mapping for repository interfaces #740 for inspiration. --> SEE Extension: CrudRestController #2736
Propose how to configure public models in such way that there is one JSON file for each model, we also need to consider how to access this file from transpiled output (
dist
vssrc
). Discuss with the wider @strongloop/loopback-next team and get an agreement. --> SEE Spike: Booter for creating REST APIs from model files #2738 (spike)Implement TestSandbox.writeTextFile --> SEE Implement TestSandbox.writeTextFile #3731
Improve
defineCrudRestController
to create a named controller class --> SEE ImprovedefineCrudRestController
to create a named controller class #3732Add
defineCrudRepositoryClass
- a helper to create a named repository class --> SEE AdddefineCrudRepositoryClass
- a helper to create a named repository class #3733Model API booter & builder --> SEE Model API booter & builder #3736
Add CrudRestApiBuilder to
@loopback/rest-crud
--> SEE Add CrudRestApiBuilder to@loopback/rest-crud
#3737Example app showing CrudRestApiBuilder --> SEE Example app showing CrudRestApiBuilder #3738
A new CLI command that will ask the user for an existing model, an existing datasource, and then generate the corresponding
public-models.json
entry. Ideally, the pattern (crud vs key-value) should be automatically inferred from the datasource config (connector type). --> SEE CLI command to expose REST API of a Model with no custom classes #2739Documentation for application developers (consuming this feature) and extension developers (building custom controller/repository classes) --> SEE Docs for exposing REST API of a Model with no custom classes #2740
Blog post - let's write it as part of this GH issue, once all other work is done, but before the Epic is called done.
Out of scope
A new extension providing KeyValueController class --> SEE Extension: KeyValueRestController #2737
Relations, see [EPIC] From relation definition to REST API with auto-generated repository/controller classes #2483
Hooks and customization (both levels - controller & repository). If the behavior provided by CrudController/KeyValueController and DefaultCrudRepository/DefaultKeyValueRepository is no longer enough, then developers should migrate the model from
public-model.json
to custom repository & controller class.Exposing custom methods contributed by individual connectors on top of the default CRUD/KeyValue REST API. See Expose custom remote methods contributed by CRUD connectors on top of DAO methods #2482
The text was updated successfully, but these errors were encountered: