diff --git a/packages/create-sitecore-jss/src/templates/nextjs/next.config.js b/packages/create-sitecore-jss/src/templates/nextjs/next.config.js index 334deb0287..73b05d9359 100644 --- a/packages/create-sitecore-jss/src/templates/nextjs/next.config.js +++ b/packages/create-sitecore-jss/src/templates/nextjs/next.config.js @@ -50,6 +50,11 @@ const nextConfig = { source: '/layouts/system/:path*', destination: `${jssConfig.sitecoreApiHost}/layouts/system/:path*`, }, + // healthz check + { + source: '/healthz', + destination: '/api/healthz', + } ]; }, }; diff --git a/packages/create-sitecore-jss/src/templates/nextjs/src/pages/api/healthz.ts b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/api/healthz.ts new file mode 100644 index 0000000000..db889f10f8 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/nextjs/src/pages/api/healthz.ts @@ -0,0 +1,12 @@ +import { HealthcheckMiddleware } from '@sitecore-jss/sitecore-jss-nextjs/monitoring'; + +/** + * This Next.js API route is used to handle healthz check request. + * By default this is used only by Sitecore XM Cloud (when running as editing host), + * but could be used in other deployment scenarios. +*/ + +// Wire up the HealthcheckMiddleware handler +const handler = new HealthcheckMiddleware().getHandler(); + +export default handler; diff --git a/packages/sitecore-jss-nextjs/monitoring.d.ts b/packages/sitecore-jss-nextjs/monitoring.d.ts new file mode 100644 index 0000000000..01e5862435 --- /dev/null +++ b/packages/sitecore-jss-nextjs/monitoring.d.ts @@ -0,0 +1 @@ +export * from './types/monitoring/index'; diff --git a/packages/sitecore-jss-nextjs/monitoring.js b/packages/sitecore-jss-nextjs/monitoring.js new file mode 100644 index 0000000000..4a42c92aeb --- /dev/null +++ b/packages/sitecore-jss-nextjs/monitoring.js @@ -0,0 +1 @@ +module.exports = require('./dist/cjs/monitoring/index'); diff --git a/packages/sitecore-jss-nextjs/package.json b/packages/sitecore-jss-nextjs/package.json index b55ff8d6d6..c8dc4255af 100644 --- a/packages/sitecore-jss-nextjs/package.json +++ b/packages/sitecore-jss-nextjs/package.json @@ -11,7 +11,7 @@ "test": "mocha --require ./test/setup.js \"./src/**/*.test.ts\" \"./src/**/*.test.tsx\" --exit", "prepublishOnly": "npm run build", "coverage": "nyc npm test", - "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-nextjs --entryPoints src/index.ts --entryPoints src/editing/index.ts --entryPoints src/middleware/index.ts --githubPages false" + "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --readme none --out ../../ref-docs/sitecore-jss-nextjs --entryPoints src/index.ts --entryPoints src/monitoring/index.ts --entryPoints src/editing/index.ts --entryPoints src/middleware/index.ts --githubPages false" }, "engines": { "node": ">=12", diff --git a/packages/sitecore-jss-nextjs/src/monitoring/healthcheck-middleware.test.ts b/packages/sitecore-jss-nextjs/src/monitoring/healthcheck-middleware.test.ts new file mode 100644 index 0000000000..d0fbd4e5d6 --- /dev/null +++ b/packages/sitecore-jss-nextjs/src/monitoring/healthcheck-middleware.test.ts @@ -0,0 +1,39 @@ +import { expect, use } from 'chai'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { spy } from 'sinon'; +import sinonChai from 'sinon-chai'; +import { HealthcheckMiddleware } from './healthcheck-middleware'; + +use(sinonChai); + +const mockRequest = () => { + return { + method: 'GET', + } as NextApiRequest; +}; + +const mockResponse = () => { + const res = {} as NextApiResponse; + res.status = spy(() => { + return res; + }); + res.send = spy(() => { + return res; + }); + return res; +}; + +describe('HealthcheckMiddleware', () => { + it('should handle request', async () => { + const req = mockRequest(); + const res = mockResponse(); + + const middleware = new HealthcheckMiddleware(); + const handler = middleware.getHandler(); + + await handler(req, res); + + expect(res.status).to.have.been.calledOnceWith(200); + expect(res.send).to.have.been.calledOnceWith('Healthy'); + }); +}); diff --git a/packages/sitecore-jss-nextjs/src/monitoring/healthcheck-middleware.ts b/packages/sitecore-jss-nextjs/src/monitoring/healthcheck-middleware.ts new file mode 100644 index 0000000000..22104d28fc --- /dev/null +++ b/packages/sitecore-jss-nextjs/src/monitoring/healthcheck-middleware.ts @@ -0,0 +1,18 @@ +import { NextApiResponse, NextApiRequest } from 'next'; + +/** + * Middleware / handler for use in healthcheck Next.js API route (e.g. '/api/healthz'). + */ +export class HealthcheckMiddleware { + /** + * Gets the Next.js API route handler + * @returns route handler + */ + public getHandler(): (req: NextApiRequest, res: NextApiResponse) => Promise { + return this.handler; + } + + private handler = async (_req: NextApiRequest, res: NextApiResponse): Promise => { + res.status(200).send('Healthy'); + }; +} diff --git a/packages/sitecore-jss-nextjs/src/monitoring/index.ts b/packages/sitecore-jss-nextjs/src/monitoring/index.ts new file mode 100644 index 0000000000..f5ba9c779c --- /dev/null +++ b/packages/sitecore-jss-nextjs/src/monitoring/index.ts @@ -0,0 +1 @@ +export { HealthcheckMiddleware } from './healthcheck-middleware'; diff --git a/packages/sitecore-jss-nextjs/tsconfig.json b/packages/sitecore-jss-nextjs/tsconfig.json index 030453e0d4..1ab721015b 100644 --- a/packages/sitecore-jss-nextjs/tsconfig.json +++ b/packages/sitecore-jss-nextjs/tsconfig.json @@ -16,6 +16,7 @@ "dist", "middleware.d.ts", "editing.d.ts", + "monitoring.d.ts", "src/tests/*", "src/test-data/*", "**/*.test.ts",