Skip to content

Serverless Functions

ChatPage runs using Next.js, and supports Server Actions and Serverless Functions. Files marked with route.tsx will execute serverless functions, while files with the 'use server' directive enable Server Actions. Both allow you to execute code using sensitive keys to specified third party services, such as stripe.com, openai.com, and other whitelisted domains.

Your functions have access to:

  • process.env: Environment variables from your project
  • params: URL parameters from dynamic routes
  • query: Query string parameters from the request URL
  • body: Request body data (for POST, PUT, PATCH requests)

Use the Vercel AI SDK to create streaming responses for real-time AI interactions.

app/api/chat/route.tsx
import { streamText, convertToModelMessages, stepCountIs } from 'ai';
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { tools } from '@/chat.config';
import { DEFAULT_MODEL_ID, getModelById } from '@/lib/llm-models';
import { insertMessages } from '@/actions/supabase/messages';
const openrouter = createOpenRouter({
apiKey: process.env.CHATPAGE_API_KEY!,
baseURL: 'https://cloud.frontend.co/api/v1',
});
export async function POST(request: Request) {
try {
const chatId = request.headers.get('x-chat-id');
const { messages, modelId } = await request.json();
const selectedModel = getModelById(modelId) ? modelId : DEFAULT_MODEL_ID;
const model = openrouter(selectedModel);
const systemMessage = {
role: 'system',
parts: [{ type: 'text', text: 'You are an AI assistant.' }],
};
const allMessages = [systemMessage, ...messages];
const convertedMessages = await convertToModelMessages(allMessages);
// Get the last user message for persistence
const lastUserMessage = [...messages].reverse().find((m: { role: string }) => m.role === 'user');
const result = await streamText({
model,
messages: convertedMessages,
tools,
stopWhen: stepCountIs(20),
providerOptions: {
openrouter: {
reasoning: {
enabled: true
}
}
},
onFinish: async ({ text }) => {
if (!chatId) return;
const messagesToInsert = [];
if (lastUserMessage) {
messagesToInsert.push({
chat_id: chatId,
role: 'user',
parts: lastUserMessage.parts,
});
}
if (text) {
messagesToInsert.push({
chat_id: chatId,
role: 'assistant',
parts: [{ type: 'text', text }],
});
}
if (messagesToInsert.length > 0) {
try {
await insertMessages(messagesToInsert);
} catch (error) {
console.error('Failed to persist messages:', error);
}
}
},
});
return result.toUIMessageStreamResponse({
sendReasoning: true
})
} catch (error) {
console.error('Chat API error:', error);
return new Response(
JSON.stringify({ error: 'Failed to process chat request' }),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
}
);
}
}

Connect to a Neon Postgres database to run SQL queries.

app/api/users/route.tsx
import { NextRequest, NextResponse } from 'next/server';
import { neon } from '@neondatabase/serverless';
const connectionString = `postgresql://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}/${process.env.DB_TABLE}`;
export async function GET(request: NextRequest) {
const sql = neon(connectionString);
const users = await sql`SELECT id, name, email FROM users LIMIT 10`;
return NextResponse.json({ users });
}
export async function POST(request: NextRequest) {
const sql = neon(connectionString);
const { name, email } = await request.json();
const result = await sql`
INSERT INTO users (name, email)
VALUES (${name}, ${email})
RETURNING id, name, email
`;
return NextResponse.json({ user: result[0] });
}

Use Server Actions to execute server-side code directly from your components without creating API routes.

actions/execute-sql.ts
'use server';
import { neon } from '@neondatabase/serverless';
const connectionString = `postgresql://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}/${process.env.DB_TABLE}`;
export async function executeQuery(query: string) {
try {
const sql = neon(connectionString);
const result = await sql`${query}`;
return { data: result };
} catch (error: any) {
console.error('Database query error:', error);
return { error: error.message || 'Failed to execute query' };
}
}
export async function getUsers() {
const sql = neon(connectionString);
const users = await sql`SELECT id, name, email FROM users LIMIT 10`;
return { users };
}
export async function createUser(name: string, email: string) {
const sql = neon(connectionString);
const result = await sql`
INSERT INTO users (name, email)
VALUES (${name}, ${email})
RETURNING id, name, email
`;
return { user: result[0] };
}