Skip to main content

Fetch Runtimes

The default @ts-rest/serverless/fetch handler is able to handle and route requests for any runtime that follows the WinterCG Minimum Common Web Platform API.

This includes but is not limited to:

  • Cloudflare Workers
  • Netlify Functions
  • Vercel Functions

Platform Context

Some platforms provide additional context arguments that you may want to pass to your handler functions. You need to first instantiate a router object with the correct context type, and then pass it with that type to the fetchRequestHandler function.

import { fetchRequestHandler, tsr } from '@ts-rest/serverless/fetch';
import { contract } from './contract';

export const router = tsr
.platformContext<{ customContext: PlatformContext }>()
.router(contract, {
getPost: async ({ params: { id } }, { customContext }) => {
console.log(platformContext);
return {
status: 200,
body: {
id: id,
title: 'Hello, World!',
},
};
},
});

Handler Examples

Cloudflare Workers

import type { Request as WorkerRequest, ExecutionContext, KVNamespace } from '@cloudflare/workers-types/experimental';
import { fetchRequestHandler, tsr } from '@ts-rest/serverless/fetch';
import { contract } from './contract';

interface Env {
MY_KV_NAMESPACE: KVNamespace;
}

const router = tsr
.platformContext<{
workerRequest: WorkerRequest;
workerEnv: Env;
workerContext: ExecutionContext
}>()
.router(contract, {
getPost: async ({ params: { id } }, { workerRequest, workerEnv, workerContext }) => {
return {
status: 200,
body: {
id: id,
title: await workerEnv.MY_KV_NAMESPACE.get(`post_${id}`),
country: workerRequest.cf?.country,
},
};
},
});

export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
return fetchRequestHandler({
request,
contract,
router,
platformContext: {
workerRequest: request as unknown as WorkerRequest,
workerEnv: env,
workerContext: ctx,
},
});
},
};

Netlify Functions

import type { Context } from '@netlify/functions';
import { fetchRequestHandler, tsr } from '@ts-rest/serverless/fetch';
import { contract } from './contract';

const router = tsr
.platformContext<{ netlifyContext: Context }>()
.router(contract, {
getPost: async ({ params: { id } }, { netlifyContext }) => {
return {
status: 200,
body: {
id: id,
title: 'Hello, World!',
ip: netlifyContext.ip,
},
};
},
});

export default async (request: Request, netlifyContext: Context) => {
return fetchRequestHandler({
request,
contract,
router,
platformContext: {
netlifyContext,
},
});
}

Vercel Functions

import type { RequestContext } from '@vercel/edge';
import { fetchRequestHandler, tsr } from '@ts-rest/serverless/fetch';
import { contract } from './contract';

const router = tsr
.platformContext<{ vercelContext: RequestContext }>()
.router(contract, {
getPost: async ({ params: { id } }, { vercelContext }) => {
return {
status: 200,
body: {
id: id,
title: 'Hello, World!',
},
};
},
});

const handler = async (request: Request, vercelContext: RequestContext) => {
return fetchRequestHandler({
request,
contract,
router,
platformContext: {
vercelContext,
},
});
};

export {
handler as GET,
handler as POST,
handler as PUT,
handler as PATCH,
handler as DELETE,
};