I'm working on a Next.js app and trying to integrate Pusher for real-time updates with authentication. However, I'm having trouble accessing the socket_id and channel_name in my API route; they both appear as undefined.
I've tried retrieving these values from both the query parameters and the request body, but they are always undefined. I've searched for examples online but mostly found examples using the old pages directory, not the new app directory in Next.js.
Here's my code:
pusherClient.ts:
"use client";
import Pusher from "pusher-js";
import { createClient } from "@/utils/supabase/client";
const supabase = createClient();
const session = supabase.auth.getSession();
const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY ?? '', {
cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER ?? '',
authEndpoint: "/api/pusher/auth",
auth: {
headers: {
'Authorization': `Bearer ${session?.then(session => session.data.session?.access_token)}`
}
},
});
export default pusher;
app/api/pusher/auth/route.ts:
import { NextRequest, NextResponse } from "next/server";
import Pusher from "pusher";
import { createClient } from "@/utils/supabase/server";
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID!,
key: process.env.PUSHER_KEY!,
secret: process.env.PUSHER_SECRET!,
cluster: process.env.PUSHER_CLUSTER!,
useTLS: true,
});
export async function POST(req: NextRequest) {
console.log("Handler invoked");
try {
const url = new URL(req.url);
console.log(url.searchParams.forEach((value, key) => console.log(`${key}: ${value}`)));
const socketId = url.searchParams.get('socket_id');
const channelName = url.searchParams.get('channel_name');
console.log("Socket ID:", socketId);
console.log("Channel Name:", channelName);
const contentType = req.headers.get('content-type') || '';
if (!contentType.includes('application/json')) {
return NextResponse.json({ error: 'Invalid content type' }, { status: 400 });
}
const { socket_id, channel_name } = await req.json();
console.log("Socket ID:", socket_id);
console.log("Channel Name:", channel_name);
const authorizationHeader = req.headers.get('authorization');
const token = authorizationHeader?.split(' ')[1];
if (!token) {
console.error("Missing auth token");
return NextResponse.json({ error: "Missing auth token" }, { status: 401 });
}
console.log("Token:", token);
const supabase = createClient();
const { data: { user }, error } = await supabase.auth.getUser(token);
if (error || !user) {
console.error("Invalid or expired token", error);
return NextResponse.json({ error: "Invalid or expired token" }, { status: 401 });
}
const userId = user.id;
console.log("User ID:", userId);
if (channel_name === `private-user-${userId}`) {
const auth = pusher.authorizeChannel(socket_id, channel_name);
return NextResponse.json(auth);
} else {
console.error("Forbidden: User not authorized for channel", channel_name);
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
} catch (error) {
console.error("Internal Server Error", error);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}
pusher.ts (server side)
"use server";
import Pusher from "pusher";
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID ?? '',
key: process.env.PUSHER_KEY ?? '',
secret: process.env.PUSHER_SECRET ?? '',
cluster: process.env.PUSHER_CLUSTER ?? '',
useTLS: true,
});
export default pusher;
Fixed, request body is in application/x-www-form-urlencoded format rather than application/json
export async function POST(req: NextRequest) {
console.log("Handler invoked");
try {
// Get socket_id and channel_name from body form
let socket_id, channel_name;
if (req.headers.get('content-type') === 'application/json') {
// Parse JSON body
const body = await req.json();
socket_id = body.socket_id;
channel_name = body.channel_name;
} else if (req.headers.get('content-type') === 'application/x-www-form-urlencoded') {
// Parse URL-encoded body
const body = await req.text();
const params = new URLSearchParams(body);
socket_id = params.get('socket_id');
channel_name = params.get('channel_name');
} else {
return NextResponse.json({ error: 'Unsupported content type' }, { status: 415 });
}
console.log("Socket ID:", socket_id);
console.log("Channel Name:", channel_name);
return NextResponse.json({ auth: pusher.authorizeChannel(socket_id, channel_name) });
} catch (error) {
console.error("Internal Server Error", error);
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}