Last active
August 13, 2025 08:28
-
-
Save MehmetAdemi/a96784de92ee91e4907bdedb20e6b90c to your computer and use it in GitHub Desktop.
Next.js App Router - Backend caption route in the Remotion Editor Starter (https://remotion.dev/docs/editor-starter/backend-routes)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // src/app/api/render/route.ts | |
| // Video rendering and export | |
| import { | |
| renderMediaOnLambda, | |
| speculateFunctionName, | |
| } from '@remotion/lambda/client'; | |
| import {NextRequest, NextResponse} from 'next/server'; | |
| import { | |
| RenderVideoPayload, | |
| RenderVideoResponse, | |
| } from '@/editor/rendering/types'; | |
| import {requireServerEnv} from '@/editor/utils/server-env'; | |
| import {collectFontInfoFromItems} from '@/editor/utils/text/collect-font-info-from-items'; | |
| import { | |
| COMP_NAME, | |
| DISK_SIZE_IN_MB, | |
| getEditorExportFileName, | |
| MEM_SIZE_IN_MB, | |
| SITE_NAME, | |
| TIMEOUT_IN_SECONDS, | |
| } from '@/remotion/constants'; | |
| import {CompositionWithContextsProps} from '@/remotion/main'; | |
| export async function POST(request: NextRequest) { | |
| try { | |
| const serverEnv = requireServerEnv(); | |
| const body = (await request.json()) as RenderVideoPayload; | |
| if (!Number.isFinite(body.compositionHeight)) { | |
| throw new Error('compositionHeight is not a number'); | |
| } | |
| if (!Number.isFinite(body.compositionWidth)) { | |
| throw new Error('compositionWidth is not a number'); | |
| } | |
| if (!Array.isArray(body.tracks)) { | |
| throw new Error('tracks is not an array'); | |
| } | |
| // Validate codec parameter | |
| if (!body.codec || (body.codec !== 'h264' && body.codec !== 'vp8')) { | |
| throw new Error('codec must be either "h264" or "vp8"'); | |
| } | |
| const inputProps: CompositionWithContextsProps = { | |
| compositionHeight: body.compositionHeight, | |
| compositionWidth: body.compositionWidth, | |
| assets: body.assets, | |
| items: body.items, | |
| tracks: body.tracks, | |
| fontInfos: collectFontInfoFromItems(Object.values(body.items)), | |
| }; | |
| const {bucketName, renderId} = await renderMediaOnLambda({ | |
| codec: body.codec, | |
| inputProps, | |
| composition: COMP_NAME, | |
| functionName: speculateFunctionName({ | |
| diskSizeInMb: DISK_SIZE_IN_MB, | |
| memorySizeInMb: MEM_SIZE_IN_MB, | |
| timeoutInSeconds: TIMEOUT_IN_SECONDS, | |
| }), | |
| region: serverEnv.REMOTION_AWS_REGION, | |
| serveUrl: SITE_NAME, | |
| downloadBehavior: { | |
| fileName: getEditorExportFileName(body.codec), | |
| type: 'download', | |
| }, | |
| }); | |
| const response: RenderVideoResponse = { | |
| type: 'success', | |
| bucketName, | |
| renderId, | |
| }; | |
| return NextResponse.json(response); | |
| } catch (e) { | |
| // eslint-disable-next-line no-console | |
| console.error('Render API error:', e); | |
| const response: RenderVideoResponse = { | |
| type: 'error', | |
| error: e instanceof Error ? e.message : 'Render service unavailable', | |
| }; | |
| return NextResponse.json(response, {status: 400}); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment