-
Notifications
You must be signed in to change notification settings - Fork 383
Move RestServer config out of async start
#621
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
Conversation
9ae1b5d to
30cdc4c
Compare
bajtos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great!
| ``` | ||
|
|
||
| You can then bind the full spec to the application using `server.spec()`. This is done on the server level, because each server instance can expose a different (sub)set of API. | ||
| You can then bind the full spec to the application using `app.api()`. Normally, this is done on the server level because each server instance can expose a different (sub)set of API, but since `RestApplication` uses only one REST server, you can bind the spec at application level. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am proposing a slightly different phrasing:
You can then bind the full spec to the application using `app.api()`.
This works well for applications with a single REST server, because
there is only one API definition involved.
If you are building an application with multiple REST servers,
where each server provides a different API, then you need
to call `server.api()` instead.
pages/en/lb4/Routes.md
Outdated
| The example below defines a `Route` that will be matched for `GET /`. When the `Route` is matched, the `greet` Operation (above) will be called. It accepts an OpenAPI [OperationObject](https://github.com/OAI/OpenAPI-Specification/blob/0e51e2a1b2d668f434e44e5818a0cdad1be090b4/versions/2.0.md#operationObject) which is defined using `spec`. | ||
| The route is then attached to a valid server context running underneath the | ||
| application. | ||
| application. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing space, please remove.
pages/en/lb4/Routes.md
Outdated
| const server = await app.getServer(RestServer); | ||
| const route = new Route('get', '/', spec, greet); | ||
| server.route(route); | ||
| app.route(route); // attaches route to RestServer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move route registration out of async start() function.
I think we no longer need async start function and can call app.start() directly:
// greet is a basic operation
function greet(name: string) {
return `hello ${name}`;
}
const route = new Route('get', '/', spec, greet);
app.route(route);
app.start();
pages/en/lb4/Sequence.md
Outdated
| (async function start() { | ||
| const server = await app.getServer(RestServer); | ||
| server.sequence(MySequence); | ||
| app.sequence(MySequence); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto, please move this line out of async start() function and consider removing the start function completely.
|
@bajtos May I get another review on this PR so that it could be landed? Thanks |
b-admike
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
I think we should remove this or create a new issue to track the removal. If it's simple just do it in this PR. This way we don't have misleading information in docs that will confuse users and provide a poor experience for anyone following the docs. |
|
You're thinking of just removing |
|
Yes getting rid of As for what |
2f6358b to
d903c1b
Compare
bajtos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but please consider addressing my first comment to simplify the WidgetApplication example.
| import {SamoflangeController, DoohickeyController} from './controllers'; | ||
| import {WidgetApi} from './apidef/'; | ||
|
|
||
| export class WidgetApplication extends Application { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, you should extend RestApplication here. The only reason why you would not want to, would be if your application was exposing multiple REST servers, which is not the case AFAICT.
This will also allow you to get rid of app.component(RestComponent) below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only problem I have with that is the page the docs is about: Application. IMO an example shown in this page should be vanilla Application instead of RestApplication. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, let's keep using Application then 👍
Could you perhaps extend Tips for application setup to mention RestApplication + when and why to use it? It's ok to leave that out of this pull request though.
| // other components to configure them before launch. | ||
| const server = await app.getServer(RestServer); | ||
| server.bind('rest.port').to(8080); | ||
| server.api(WidgetApi); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a breaking change in the context of the original example. The original example assumed api-first approach, where WidgetApi contained Swagger spec describing REST API, with extensions like x-controller-name and x-method used to link Swagger endpoints with controller method implementing them.
Since api-first support is out of scope of our upcoming release, I am ok to rework this example to code-first approach, where the REST API is described on controller classes/methods using decorators like @get and @param.
In other words, this change is fine with me too 👍
| // inject your spec here! | ||
| server.api(spec); | ||
| server.bind("rest.port").to(3001); | ||
| await super.start(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we still need to call super.start()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@b-admike I think in this case it's enough to just inherit start() function from server. No need to override it.
| const route = new Route('get', '/', spec, greet); | ||
| app.route(route); // attaches route to RestServer | ||
|
|
||
| app.start(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't app.start() returning a promise and thus should be called with await? (see https://github.com/strongloop/loopback-next/blob/master/packages/core/src/application.ts#L128-L136)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that's a myth.
-
awaitcan only be used within anasyncfunction. Theawaitkeyword helps VM to suspend the execution and wait for the resolution/rejection of the promise or value. -
In this case, you cannot use
awaitas the module will be wrapped into a function by Node.js but it's notasync. -
The code before the change declares an
asyncfunction and execute it immediately. -
It's perfectly fine to call an
asyncfunction or a function returning a promise withoutawait. The return value is Promise. You have to manually call.then() or .catch()to set up result handler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification Raymond. We don't really need to show .then() or .catch() handlers in this example. Disregard my comment!
| server.sequence(MySequence); | ||
| await app.start(); | ||
| })(); | ||
| app.start(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
jannyHou
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as @b-admike : https://github.com/strongloop/loopback.io/pull/621/files#r171920566
Other than that LGTM 👍 awesome clarification of the current behavior of rest server.
| const route = new Route('get', '/', spec, greet); | ||
| app.route(route); // attaches route to RestServer | ||
|
|
||
| app.start(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification Raymond. We don't really need to show .then() or .catch() handlers in this example. Disregard my comment!
async startasync start
This PR would utilize the new changes made in loopbackio/loopback-next#994 so that
async startis not overridden if it can be overridden.I left a couple of decisions undecided because I wasn't sure whether i should be changing some of these or not. Here are my main concerns:
WidgetApplicationexample underApplication.md,server.api(WidgetApi)is used in start. The function call can't be moved into the constructor since the app is only extending fromApplicationand notRestApplication;api()function can't be called because it doesn't exist for the application. Should the example be left as it is?Context.md, we configure multiple servers inside the start function. I think this should be left as is, but does anybody think otherwise?