Skip to content

Commit b4d9bc5

Browse files
committed
feat(rest): add config option to disable API Explorer redirects
1 parent 4b8f877 commit b4d9bc5

File tree

3 files changed

+62
-15
lines changed

3 files changed

+62
-15
lines changed

docs/site/Server.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,21 @@ const app = new RestApplication({
124124
});
125125
```
126126

127+
#### Disable redirect to API Explorer
128+
129+
To disable redirect to the externally hosted API Explorer, set the config option
130+
`rest.apiExplorer.disabled` to `true`.
131+
132+
````ts
133+
const app = new RestApplication({
134+
rest: {
135+
apiExplorer: {
136+
disabled: true,
137+
},
138+
},
139+
});
140+
```
141+
127142
### Enable HTTPS
128143

129144
Enabling HTTPS for the LoopBack REST server is just a matter of specifying the
@@ -153,7 +168,7 @@ export async function main() {
153168
const url = app.restServer.url;
154169
console.log(`Server is running at ${url}`);
155170
}
156-
```
171+
````
157172
158173
### Customize CORS
159174

packages/rest/src/rest.server.ts

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,8 @@ export class RestServer extends Context implements Server, HttpServerLike {
165165
config.openApiSpec = config.openApiSpec || {};
166166
config.openApiSpec.endpointMapping =
167167
config.openApiSpec.endpointMapping || OPENAPI_SPEC_MAPPING;
168-
config.apiExplorer = config.apiExplorer || {};
169168

170-
const url = config.apiExplorer.url || 'https://explorer.loopback.io';
171-
172-
config.apiExplorer.httpUrl =
173-
config.apiExplorer.httpUrl ||
174-
config.apiExplorer.url ||
175-
'http://explorer.loopback.io';
176-
177-
config.apiExplorer.url = url;
169+
config.apiExplorer = normalizeApiExplorerConfig(config.apiExplorer);
178170

179171
this.config = config;
180172
this.bind(RestBindings.PORT).to(config.port);
@@ -251,7 +243,16 @@ export class RestServer extends Context implements Server, HttpServerLike {
251243
this._serveOpenApiSpec(req, res, mapping[p]),
252244
);
253245
}
254-
this._expressApp.get(['/swagger-ui', '/explorer'], (req, res) =>
246+
247+
const explorerConfig = this.config.apiExplorer || {};
248+
if (explorerConfig.disabled) {
249+
debug('Redirect to swagger-ui was disabled by configuration.');
250+
return;
251+
}
252+
253+
const explorerPaths = ['/swagger-ui', '/explorer'];
254+
debug('Setting up redirect to swagger-ui. URL paths: %j', explorerPaths);
255+
this._expressApp.get(explorerPaths, (req, res) =>
255256
this._redirectToSwaggerUI(req, res),
256257
);
257258
}
@@ -444,11 +445,9 @@ export class RestServer extends Context implements Server, HttpServerLike {
444445
}
445446

446447
private async _redirectToSwaggerUI(request: Request, response: Response) {
448+
const config = this.config.apiExplorer!;
447449
const protocol = this._getProtocolForRequest(request);
448-
const baseUrl =
449-
protocol === 'http'
450-
? this.config.apiExplorer!.httpUrl
451-
: this.config.apiExplorer!.url;
450+
const baseUrl = protocol === 'http' ? config.httpUrl : config.url;
452451
const openApiUrl = `${this._getUrlForClient(request)}/openapi.json`;
453452
const fullUrl = `${baseUrl}?url=${openApiUrl}`;
454453
response.redirect(308, fullUrl);
@@ -822,13 +821,20 @@ export interface ApiExplorerOptions {
822821
* default to https://loopback.io/api-explorer
823822
*/
824823
url?: string;
824+
825825
/**
826826
* URL for the API explorer served over `http` protocol to deal with mixed
827827
* content security imposed by browsers as the spec is exposed over `http` by
828828
* default.
829829
* See https://github.com/strongloop/loopback-next/issues/1603
830830
*/
831831
httpUrl?: string;
832+
833+
/**
834+
* Set this flag to disable the built-in redirect to externally
835+
* hosted API Explorer UI.
836+
*/
837+
disabled?: true;
832838
}
833839

834840
/**
@@ -848,3 +854,17 @@ export interface RestServerOptions {
848854
* @interface RestServerConfig
849855
*/
850856
export type RestServerConfig = RestServerOptions & HttpServerOptions;
857+
858+
function normalizeApiExplorerConfig(
859+
input: ApiExplorerOptions | undefined,
860+
): ApiExplorerOptions {
861+
const config = input || {};
862+
const url = config.url || 'https://explorer.loopback.io';
863+
864+
config.httpUrl =
865+
config.httpUrl || config.url || 'http://explorer.loopback.io';
866+
867+
config.url = url;
868+
869+
return config;
870+
}

packages/rest/test/integration/rest.server.integration.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,18 @@ paths:
437437
expect(response.get('Access-Control-Allow-Credentials')).to.equal('true');
438438
});
439439

440+
it('can be configured to disable "GET /explorer"', async () => {
441+
const server = await givenAServer({
442+
rest: {
443+
...givenHttpServerConfig(),
444+
apiExplorer: {disabled: true},
445+
},
446+
});
447+
448+
const request = createClientForHandler(server.requestHandler);
449+
await request.get('/explorer').expect(404);
450+
});
451+
440452
it('honors "x-forwarded-*" headers', async () => {
441453
const app = new Application();
442454
app.component(RestComponent);

0 commit comments

Comments
 (0)