I managed to run atlassian-connect-expresss
in NestJS
without modifying the library; the trick was to provide the express http adapter (after it’s been modified by atlassian-connect-express
) to NestFactory.create
.
This is what I used (make sure to also see the readme in atlassian-connect-express
for more context):
npm install -g @nestjs/cli
nest new project-name; cd project-name
npm install atlassian-connect-express compression cookie-parser helmet morgan errorhandler nocache --save
npm install ngrok sqlite3 --save-dev
- Copy/modify
credentials.json
, config.json
and atlassian-connect.json
(see the readme in atlassian-connect-express
)
- in
src/main.ts
:
import { NestFactory } from '@nestjs/core';
import {
ExpressAdapter,
NestExpressApplication,
} from '@nestjs/platform-express';
import * as atlassianConnect from 'atlassian-connect-express';
import * as compression from 'compression';
import * as cookieParser from 'cookie-parser';
import * as errorHandler from 'errorhandler';
import * as express from 'express';
import * as helmet from 'helmet';
import * as morgan from 'morgan';
import * as nocache from 'nocache';
import * as path from 'path';
import { AppModule } from './app.module';
import { AtlassianConnectService } from './atlassian-connect/atlassian-connect.service';
async function bootstrap() {
const expressAdapter = new ExpressAdapter();
// Atlassian security policy requirements
// http://go.atlassian.com/security-requirements-for-cloud-apps
// HSTS must be enabled with a minimum age of at least one year
expressAdapter.use(
helmet.hsts({
maxAge: 31536000,
includeSubDomains: false,
}),
);
expressAdapter.use(
helmet.referrerPolicy({
policy: ['origin'],
}),
);
expressAdapter.use(express.json());
expressAdapter.use(express.urlencoded({ extended: false }));
expressAdapter.use(cookieParser());
// Gzip responses when appropriate
expressAdapter.use(compression());
// Atlassian security policy requirements
// http://go.atlassian.com/security-requirements-for-cloud-apps
expressAdapter.use(nocache());
const aceInstance = atlassianConnect(expressAdapter);
// Include atlassian-connect-express middleware
expressAdapter.use(aceInstance.middleware());
const app = await NestFactory.create<NestExpressApplication>(
AppModule,
expressAdapter,
);
// retrieve the express server
const httpAdapter = app.getHttpAdapter();
const expressApp = httpAdapter.getInstance();
// See config.json
const port = aceInstance.config.port();
app.set('port', port);
const devEnv = expressApp.get('env') === 'development';
app.use(morgan(devEnv ? 'dev' : 'combined'));
// Configure Handlebars
app.setBaseViewsDir(path.join(__dirname, 'views'));
app.setViewEngine('hbs');
// Show nicer errors in dev mode
if (devEnv) app.use(errorHandler());
// Initalize the injectable NestJS Service that lets you access the instance of `atlassian-connect-express`
const atlassianConnectService = app.get(AtlassianConnectService);
atlassianConnectService.initialization(aceInstance);
// Boot the HTTP server
app.listen(port, () => {
console.log('App server running on port' + port);
// Enables auto registration/de-registration of app into a host in dev mode
if (devEnv) aceInstance.register();
});
}
bootstrap();
- In
src/atlassian-connect/atlassian-connect.service
:
import { Injectable } from '@nestjs/common';
@Injectable()
export class AtlassianConnectService {
constructor() {}
/**
* `Atlassian Connect Express` (ACE) instance
*/
private aceInstance: any;
/**
* Initialize the service with the `Atlassian Connect Express` (ACE) instance
*
* @param instance
*/
initialization(instance) {
this.aceInstance = instance;
}
getInstance() {
return this.aceInstance;
}
}
- In
src/app.module.ts
:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AtlassianConnectService } from './atlassian-connect/atlassian-connect.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService, AtlassianConnectService],
})
export class AppModule {}
Done! Now you can inject AtlassianConnectService and do things like
constructor(atlassianConnectService: AtlassianConnectService){}
...
const addon = this.atlassianConnectService.getInstance()
addon.authenticate()(req,res,next) // verifies that the request was sent from Atlassian, and not altered
const httpClient = addon.httpClient({ clientKey })
httpClent.post(url) // sends a post request with authentication for the specific client
Thanks to @FrankyFrank and @Farid for posting the example that I started this off of.