Skip to content

Make lambda easier to install on express #4722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/docs/components/TableOfContents/lambda.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ export const TableOfContents: React.FC = () => {
Handle incoming webhooks specifically for the Next.js pages router
</div>
</TOCItem>
<TOCItem link="/docs/lambda/expresswebhook">
<strong>expressWebhook()</strong>
<div>
Handle incoming webhooks specifically for Express.js
</div>
</TOCItem>
</Grid>
</div>
);
Expand Down
67 changes: 67 additions & 0 deletions packages/docs/docs/lambda/expresswebhook.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
image: /generated/articles-docs-lambda-expresswebhook.png
id: expresswebhook
title: expressWebhook()
slug: /lambda/expresswebhook
crumb: 'Lambda API'
---

Simplifies the process of setting up a [Lambda Webhook](/docs/lambda/webhooks) in your Express.js server. See [`pagesRouterWebhook()`](/docs/lambda/pagesrouterwebhook) and [`appRouterWebhook()`](/docs/lambda/approuterwebhook) for doing the same with Next.js apps.

## API

The function accepts an object with six key-value pairs:

### `secret`

Your webhook secret, must be a `string`

### `testing`

Whether or not to allow requests intending to test the endpoint, useful while using Webhook endpoint tester on [Webhooks Page](/docs/lambda/webhooks). Should be a `boolean`.

### `extraHeaders`

Add your own custom headers to the outgoing response. Provide key-value pairs where both the key and value are strings.

### `onSuccess()`

A function that is called with a [`WebhookSuccessPayload`](/docs/lambda/webhooks#response) object as an argument when the incoming request indicates a successful event.

### `onError()`

A function that is called with a [`WebhookErrorPayload`](/docs/lambda/webhooks#response) object as an argument when the incoming request indicates an error.

### `onTimeout()`

A function that is called with a [`WebhookTimeoutPayload`](/docs/lambda/webhooks#response) object as an argument when the incoming request indicates a timeout.

## Example

Setting up a webhook endpoint in an Express.js server.

```jsx twoslash title="server.js"
import {expressWebhook} from '@remotion/lambda/client';

const handler = expressWebhook({
secret: 'mysecret',
testing: true,
extraHeaders: {
region: "south-asia"
},
onSuccess: () => console.log('Rendering Completed Successfully'),
onError: () => console.log('Something went wrong while rendering'),
onTimeout: () => console.log('Timeout occured while rendering'),
})

router.post("/webhook", jsonParser, handler);

router.options("/webhook", jsonParser, handler);
```

See [Webhooks](/docs/lambda/webhooks) for a detailed example.

## See also

- [Webhooks](/docs/lambda/webhooks)
- [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/lambda/src/api/express-webhook.ts)
105 changes: 67 additions & 38 deletions packages/docs/docs/lambda/webhooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ export type WebhookTimeoutPayload = StaticWebhookPayload & {
type: 'timeout';
};

export type WebhookPayload =
| WebhookErrorPayload
| WebhookSuccessPayload
| WebhookTimeoutPayload;
export type WebhookPayload = WebhookErrorPayload | WebhookSuccessPayload | WebhookTimeoutPayload;
```

The fields [`renderId`](/docs/lambda/rendermediaonlambda#renderid), [`bucketName`](/docs/lambda/rendermediaonlambda#bucketname) will be returned [just like they are returned by `renderMediaOnLambda()` itself](/docs/lambda/rendermediaonlambda#return-value).
Expand Down Expand Up @@ -142,59 +139,91 @@ Instead of validating the signature yourself, you can use the [`validateWebhookS

## Example webhook endpoint (Express)

You can use any web framework and language to set up your webhook endpoint. The following example is written in JavaScript using the Express framework.
You can use any web framework and language to set up your webhook endpoint. The following example is written in JavaScript using the Express framework, we are using [expressWebhook()](/docs/lambda/expresswebhook) to simplify the process.

```javascript title="server.js"
import express from "express";
import bodyParser from "body-parser";
import * as Crypto from "crypto";
import {
validateWebhookSignature,
WebhookPayload,
} from "@remotion/lambda/client";
```javascript twoslash title="server.js"
const ENABLE_TESTING = false;

// ---cut---
import express from 'express';
import bodyParser from 'body-parser';
import {expressWebhook} from '@remotion/lambda/client';

const router = express();
const jsonParser = bodyParser.json();

const handler = expressWebhook({
secret: 'mysecret',
testing: ENABLE_TESTING,
onSuccess: ({renderId}) => console.log('Finished render', renderId),
onTimeout: ({renderId}) => console.log('Time out', renderId),
onError: ({renderId}) => console.log('Error', renderId),
});

router.post('/webhook', jsonParser, handler);

router.options('/webhook', jsonParser, handler);

// You'll need to add a JSON parser middleware globally or
// for the webhook route in order to get access to the request
// body.
router.listen(3000, () => {
console.log('Server is running on port 3000');
});
```

This can also be done manually, to have the maximum control over the endpoint's logic:

```javascript twoslash title="server.js"
import express from 'express';
import bodyParser from 'body-parser';
import {validateWebhookSignature} from '@remotion/lambda/client';

const router = express();
const jsonParser = bodyParser.json();

// Enable testing through the tool below
const ENABLE_TESTING = true;
const handler = (req, res) => {
// add headers to enable testing
const ENABLE_TESTING = true;

// Express API endpoint
router.post("/my-remotion-webhook-endpoint", jsonParser, (req, res) => {
if (ENABLE_TESTING) {
res.setHeader("Access-Control-Allow-Origin", "https://www.remotion.dev");
res.setHeader("Access-Control-Allow-Methods", "OPTIONS,POST");
res.setHeader(
"Access-Control-Allow-Headers",
"X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode"
);
res.setHeader('Access-Control-Allow-Origin', 'https://www.remotion.dev');
res.setHeader('Access-Control-Allow-Methods', 'OPTIONS,POST');
res.setHeader('Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode');
}

if (req.method === "OPTIONS") {
// dont go forward if just testing
if (req.method === 'OPTIONS') {
// custom code to handle OPTIONS request
// logger(req).info('OPTIONS request received');
res.status(200).end();
return;
}


// validate the webhook signature
validateWebhookSignature({
signatureHeader: req.header("X-Remotion-Signature"),
signatureHeader: req.header('X-Remotion-Signature'),
body: req.body,
secret: process.env.WEBHOOK_SECRET as string
secret: 'mysecret',
});

const status = req.header("X-Remotion-Status"); // success, timeout, error
const mode = req.header("X-Remotion-Mode"); // demo or production

const payload = JSON.parse(req.body) as WebhookPayload;
if (payload.type === "success") {
// ...
} else if (payload.type === "timeout") {
// ...
// custom logic
const payload = req.body;
if (payload.type === 'success') {
//success logic here
} else if (payload.type === 'error') {
//error logic here
} else if (payload.type === 'timeout') {
//timeout logic here
}

// send response
res.status(200).json({success: true});
};

router.post('/webhook', jsonParser, handler);

router.options('/webhook', jsonParser, handler);

router.listen(3000, () => {
console.log('Server is running on port 3000');
});
```

Expand Down
30 changes: 15 additions & 15 deletions packages/docs/get-pages.mjs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import fs from "fs";
import path from "path";
import fs from 'fs';
import path from 'path';

export const readDir = (dir, pages = []) => {
const docs = fs.readdirSync(dir);
for (const doc of docs) {
const stat = fs.statSync(path.join(dir, doc));
if (stat.isDirectory()) {
readDir(path.join(dir, doc), pages);
} else if (stat.isFile()) {
if (doc.includes("redirect")) {
continue;
}
const docs = fs.readdirSync(dir);
for (const doc of docs) {
const stat = fs.statSync(path.join(dir, doc));
if (stat.isDirectory()) {
readDir(path.join(dir, doc), pages);
} else if (stat.isFile()) {
if (doc.includes('redirect')) {
continue;
}

pages.push(path.join(dir, doc));
}
}
pages.push(path.join(dir, doc));
}
}

return pages;
return pages.sort((a, b) => a.localeCompare(b));
};
1 change: 1 addition & 0 deletions packages/docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ module.exports = {
'lambda/validatewebhooksignature',
'lambda/approuterwebhook',
'lambda/pagesrouterwebhook',
'lambda/expresswebhook'
],
},
{
Expand Down
Loading
Loading