Serverless Functions
Frontend AI runs using Next.js, and files marked with route.tsx will execute serverless functions. Functions allow you to securely execute code that should not be visible in the browser, keeping sensitive logic and API keys protected.
Available Data
Section titled “Available Data”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 Cases
Section titled “Use Cases”Fetching Data from a REST API
Section titled “Fetching Data from a REST API”Use native fetch to retrieve data from external APIs.
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const city = searchParams.get('city');
const response = await fetch( `https://api.weather.com/v1/current?city=${city}`, { headers: { 'Authorization': `Bearer ${process.env.WEATHER_API_KEY}` } } );
const data = await response.json(); return NextResponse.json(data);}Querying a Database with Neon
Section titled “Querying a Database with Neon”Connect to a Neon Postgres database to run SQL queries.
import { NextRequest, NextResponse } from 'next/server';import { neon } from '@neondatabase/serverless';
export async function GET(request: NextRequest) { const sql = neon(process.env.DATABASE_URL!);
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(process.env.DATABASE_URL!); 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] });}Streaming AI Responses with OpenAI
Section titled “Streaming AI Responses with OpenAI”Create streaming responses for real-time AI interactions.
import { NextRequest } from 'next/server';import OpenAI from 'openai';
export async function POST(request: NextRequest) { const { messages } = await request.json();
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const stream = await openai.chat.completions.create({ model: 'gpt-5.2', messages, stream: true });
const encoder = new TextEncoder(); const readable = new ReadableStream({ async start(controller) { for await (const chunk of stream) { const text = chunk.choices[0]?.delta?.content || ''; controller.enqueue(encoder.encode(text)); } controller.close(); } });
return new Response(readable, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });}Executing Raw SQL with Drizzle ORM
Section titled “Executing Raw SQL with Drizzle ORM”Use Drizzle ORM with Neon for more advanced database operations.
import { NextResponse } from 'next/server';import { neon } from '@neondatabase/serverless';import { drizzle } from 'drizzle-orm/neon-http';import { sql } from 'drizzle-orm';
export async function POST(request: Request) { try { const { query } = await request.json();
if (!query) { return NextResponse.json( { error: 'SQL query is required' }, { status: 400 } ); }
const client = neon(process.env.DATABASE_URL!); const db = drizzle(client);
// Execute raw SQL using drizzle's sql template const result = await db.execute(sql.raw(query));
return NextResponse.json({ data: result }); } catch (error: any) { console.error('Database query error:', error); return NextResponse.json( { error: error.message || 'Failed to execute query' }, { status: 500 } ); }}Server-Side Authentication with Supabase
Section titled “Server-Side Authentication with Supabase”Verify user authentication on the server using Supabase.
import { NextResponse } from 'next/server';import { createClient } from '@supabase/supabase-js';
export async function POST(request: Request) { try { const { access_token } = await request.json(); const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
// Create a Supabase client with the user's access token const supabase = createClient(supabaseUrl, supabaseAnonKey, { global: { headers: { Authorization: `Bearer ${access_token}`, } } });
// Get the user using the access token const { data: { user }, error } = await supabase.auth.getUser(access_token);
if (error || !user) { return NextResponse.json( { error: 'Invalid or expired token' }, { status: 401 } ); }
return NextResponse.json({ user }); } catch (error: any) { return NextResponse.json( { error: error.message || 'Authentication failed' }, { status: 500 } ); }}Query Logs
Section titled “Query Logs”Each time you invoke a function, query logs are available in the Code > Functions tab. The logs help you debug and monitor your functions by showing:
- Status code: The HTTP response status (200, 400, 500, etc.)
- Payload: The request data sent to the function
- Console logs: Any
console.logoutput from your function - Response: The data returned from the server
Use these logs to verify that your functions are executing correctly and to troubleshoot any errors that occur.