# Webhooks server in Node.js

This article presents a simple [webhooks server](../../developer-resources/webhooks/server.md) example.

> **Warning**
>
> We strongly advise you NOT to use this in a production environment.

<a id="dependencies">

## Dependencies

Both examples use the Express library to create the HTTP endpoint.\
Also, for local development purposes, a tunneling service is required. This example uses [ngrok](https://ngrok.com/).

```bash
npm install express jsonwebtoken
```

You can download ngrok here: <https://ngrok.com/download>.

<a id="examples">

## Examples

Below are two examples of a webhooks server built on Express.

<a id="example-without-checking-the-request-signature">

### Example without checking the request signature

This is a very simple example, with the server logging the `body` from the request to the console. The `body` contains the complete [webhook information](../../developer-resources/webhooks/overview.md#webhook-format) sent from CKEditor Cloud Services.

```js
const express = require( 'express' );
const app = express();
app.use( express.json() );

app.post( '/', ( req, res ) => {
	console.log( 'received webhook', req.body );
	res.sendStatus( 200 );
} );

app.listen( 9000, () => console.log( 'Node.js server started on port 9000.' ) );
```

<a id="example-with-checking-the-request-signature">

### Example with checking the request signature

This example is more complex because while the server logs the webhook information to the console, it also checks if the request was sent from the CKEditor Cloud Services servers and was [signed](../../developer-resources/security/request-signature.md) with the correct [API secret](../../developer-resources/security/api-secret.md).

Several variables are needed to generate and check the signature. The [API secret](../../developer-resources/security/api-secret.md) is available in the [Customer Portal](https://portal.ckeditor.com/) for SaaS or in the Management Panel for On-Premises, the rest of the parameters are in the request:

* `method`: `req.method`
* `url`: `req.url`
* `timestamp`: `req.headers[ 'x-cs-timestamp' ]`
* `body`: `req.rawBody`

Please note that the `rawBody` field was added by the following configuration:

```js
app.use( express.json( { verify: ( req, res, buffer ) => { req.rawBody = buffer; } } ) );
```

This field is not available by default in Express. The `body` field available in Express contains the already processed data that cannot be used to generate the signature.

```js
const crypto = require( 'crypto' );

const express = require( 'express' );
const app = express();

const API_SECRET = 'secret';

app.use( express.json( { verify: ( req, res, buffer ) => { req.rawBody = buffer; } } ) );

app.post( '/', ( req, res ) => {
	const signature = _generateSignature( req.method, req.url, req.headers[ 'x-cs-timestamp' ], req.rawBody );

	if ( signature !== req.headers[ 'x-cs-signature' ] ) {
		return res.sendStatus( 401 );
	}

	console.log( 'received webhook', req.body );
	res.sendStatus( 200 );
} );

app.listen( 9000, () => console.log( 'Node.js server started on port 9000.' ) );

function _generateSignature( method, url, timestamp, body ) {
	const hmac = crypto.createHmac( 'SHA256', API_SECRET );

	hmac.update( `${ method.toUpperCase() }${ url }${ timestamp }` );

	if ( body ) {
		hmac.update( body );
	}

	return hmac.digest( 'hex' );
}
```

<a id="usage">

## Usage

Start the server with:

```bash
node index.js
```

If the server is running on port `9000`, run ngrok with:

```bash
./ngrok http 9000
```

After this, you should see a `*.ngrok.io` URL. Copy the `*.ngrok.io` URL and paste it in the webhook configuration. You should be able to receive webhooks now.

---

Full index of the Cloud Services documentation: [llms.txt](../../../llms.txt)
