One cool thing about Nest is its dedicated OpenAPI module which allows you to nearly automatically generate an OpenAPI specification for your API. You practically just have to add some decorators here and there and voila.
„The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection.“ Read more about the OpenAPI Specification here.
However OAS aims to be open by its name, making your API specifications available to everyone might not always be what you want, for example when your project‘s API is not a public one.
So what to do, when you want to benefit from OAS and the Swagger UI by only giving your team members access?
My strategy usually is protecting the Swagger UI with a password and hiding it on production entirely. This is how you could achieve that.
Getting started
Following the Nest docs, after installing all needed dependencies your main.ts could look something like this:
// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('API Docs')
.setDescription('The API documentation')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
await app.listen(3000);
}
bootstrap();
Swagger UI will be up and running visiting http://localhost:8000/docs in the browser.
Password protection
So first let’s protect the Swagger UI with HTTP basic auth requiring visitors to enter a username and password to access /docs
or /docs-json
. This can easily be done by implementing express-basic-auth, a simple plug & play HTTP basic auth middleware for Express.
npm i express-basic-auth
After installing express-basic-auth
you would want to enable this middleware for your /docs
and /docs-json
endpoints. To do so modify main.ts like the following:
// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import * as basicAuth from 'express-basic-auth';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(['/docs', '/docs-json'], basicAuth({
challenge: true,
users: {
[process.env.SWAGGER_USER]: process.env.SWAGGER_PASSWORD,
},
}));
const config = new DocumentBuilder()
.setTitle('API Docs')
.setDescription('The API documentation')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
await app.listen(3000);
}
bootstrap();
The key to getting this to work is the right order, it is important to apply the middleware app.use(['/docs', '/docs-json'], basicAuth({…})
before you initialize Swagger.
basicAuth()
in this scenario expects an object of users, I am using just one here. Keep in mind that it is always a good idea to not hardcode credentials, so relying on environment variables is a good option here. There are quite a few config options to express-basic-auth
available, just check out the docs.
Hide Swagger UI on production
As mentioned the second thing I tend to do is hiding Swagger UI entirely on production. The most simple way to do so might be by wrapping a conditional statement around the parts initializing Swagger. Check this out:
// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import * as basicAuth from 'express-basic-auth';
import { AppModule } from './app.module';
const SWAGGER_ENVS = ['local', 'dev', 'staging'];
async function bootstrap() {
const app = await NestFactory.create(AppModule);
if (SWAGGER_ENVS.includes(process.env.NODE_ENV)) {
app.use(['/docs', '/docs-json'], basicAuth({
challenge: true,
users: {
[process.env.SWAGGER_USER]: process.env.SWAGGER_PASSWORD,
},
}));
const config = new DocumentBuilder()
.setTitle('API Docs')
.setDescription('The API documentation')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
}
await app.listen(3000);
}
bootstrap();
This basically only applys the basic auth middleware and initializes Swagger when NODE_ENV
is local
, dev
or staging
. Depending on how you handle your environment names or have any other mechanism to check for a prodcution deployment, this may look slightly different in your project, but I think you get the gist.
So that's about it!
Top comments (5)
Great post, actually the only one I found! A possible security oversight:
/docs-json
is not protected by this. Therefore use an array for the basic auth['/docs', '/docs-json']
. Also I would recommend using theConfigurationService
instead of directly accessing the env variables.I adapted your solution on this stackoverflow: stackoverflow.com/questions/548028...
Thanks @kiwikilian!
I updated my post with your proposal. Honestly, I did not know about the
-json
part, it is cool though!I also was wondering why using
ConfigurationService
instead of directly accessing the env variables would be a better idea?Cool! You can use the JSON format of your OpenAPI with other generator tools for example to generate types and fetch functions for your frontend application.
The
ConfigurationService
would simply be the most NestJS way of doing it. It’s awesome to validate your envs so you don’t forget about configuring something. But it’s not necessary, just a further read: docs.nestjs.com/techniques/configu...@mahnuh another update, there is also
/docs-yaml
, so best is to secure with regex/docs*
or name all three paths explicitly.Thanks, i was searching for this so much on the internet but could not find anything. Not sure if it's just not intended to make a API doc private, but in our company we have an external app development company, so we need a public staging access to the api docs that need to be authenticated somehow... No idea why that issue hasn't found it's way into the nest docs.