Blog

Come implementare le videochiamate in Next.js

· Andrij Tkachenko · 6 min di lettura

Due finestre del browser affiancate, ognuna con l’altra persona in video e una chat accanto, unite da un cuore

Una videochiamata sembra una funzionalità sola, ma in un’app Next.js è fatta di diverse parti. Due browser devono trovarsi e mettersi d’accordo su come collegarsi. La persona chiamata deve sentirla squillare su qualunque pagina si trovi. Alcune reti non lasciano mai passare una connessione diretta. E prima di tutto questo, la tua app deve decidere chi può chiamare chi.

La maggior parte dei tutorial si ferma a due browser sulla stessa pagina che si scambiano le offer a mano. Questo articolo passa in rassegna ogni parte che serve a un’app vera, con un esempio completo e funzionante: il sito di incontri della nostra guida per Next.js, dove due iscritti si danno un appuntamento in video nella loro chat. Puoi provarlo dal vivo: scegli chi essere, poi aprilo in una finestra privata, scegli qualcun altro e chiama.

Cosa serve a una videochiamata

  • Signaling e squillo. Prima di qualsiasi immagine, i due browser si scambiano offer e candidati di rete tramite un server, e la chiamata deve squillare in ogni scheda che la persona chiamata ha aperto. Servono connessioni tenute aperte per tutto il tempo, che una funzione serverless non può mantenere (perché, e cosa fare).
  • Un percorso tra i browser. Diretto quando le loro reti lo consentono, e tramite un relay quando non lo consentono (i server STUN e TURN, spiegati).
  • Chi può chiamare chi. Una regola sul tuo server, con i dati della tua app, controllata prima di far squillare chiunque.
  • La fotocamera e il microfono. Una sola richiesta di permesso dal browser, il cambio di dispositivo, il microfono muto e la fotocamera spenta.
  • Una schermata di chiamata. Lo squillo, rispondere e rifiutare, i due video, la durata, riagganciare.
  • Media server, appena in una chiamata ci sono più di due persone (come funzionano le chiamate di gruppo).

Puoi costruire ognuna di queste parti da solo su WebRTC. Oppure lasci che un cloud in tempo reale gestisca le connessioni, lo squillo e i relay, mentre il tuo server si tiene l’unica parte davvero tua: la regola. È quello che fa il sito di incontri, con WordPlus Cloud.

L’esempio: appuntamenti in video su un sito di incontri

Gli iscritti al sito trovano le persone che cercano, vedono chi è online, chattano e si danno un appuntamento in video prima di incontrarsi di persona. È un’app Next.js con l’App Router e Postgres, e la sua parte in tempo reale sta in pochi file. Per le sue chiamate bastano quattro passi.

1. Chi può chiamare chi

Su un sito di incontri, due persone possono parlarsi se ciascuna è ciò che l’altra cerca e nessuna delle due ha bloccato l’altra. Il sito ha questa regola in un solo punto, canMeet, e una chat è un numero con i suoi membri. mayChat dice che qualcuno può usare una chat se ne è membro e può ancora incontrare tutti gli altri membri. Le chiamate si dichiarano con il nome della stanza della chat, con questa regola per entrambe le persone:

lib/realtime.ts
realtime.calls('chat:{id}', async (user, other, { id }) => {
    // The one asking: who calls, as they call, or who is called, as they answer.
    const userMayChat = await mayChat(user, id);
    // The other one of the call.
    const otherMayChat = await mayChat(other, id);
    return userMayChat && otherMayChat;
});

Viene eseguita per chi chiama, e di nuovo per chi viene chiamato quando risponde. Così un blocco ha effetto subito: dal momento in cui uno blocca l’altro, canMeet dice di no, e la chiamata successiva viene rifiutata.

2. Dove gira la regola

Quando una pagina chiede di chiamare, o di rispondere, chiede prima al server del sito stesso, in un route handler:

app/api/realtime/calls/route.ts
import { currentUserId } from '@/lib/auth';
import { realtime } from '@/lib/realtime';

export async function POST(request: Request) {
    // JSON only, which a page of another site can't send here.
    if (!request.headers.get('content-type')?.includes('application/json')) {
        return new Response(null, { status: 415 });
    }
    const ask = await request.json();
    const user = await currentUserId();
    const pass = await realtime.authorizeCall(ask, user);
    return Response.json(pass);
}

currentUserId() è l’autenticazione del sito stesso, quindi la sessione funziona qui come su qualsiasi pagina. Quando la regola dice sì, la pagina riceve un lasciapassare firmato con il segreto dell’app, e con quello WordPlus Cloud fa squillare l’altra persona. La route risponde con un’unica richiesta breve, che è esattamente ciò che l’hosting serverless sa fare bene.

3. Lo squillo su ogni pagina

Il layout delle pagine per utenti autenticati si collega una volta sola e renderizza la schermata di chiamata accanto alle pagine:

app/(app)/screens.tsx
'use client';

import '@wordplus/realtime-ui/calls.css';
import '@wordplus/realtime-ui/chat.css';
import { useRealtime } from '@wordplus/realtime/react';
import { CallScreen } from '@wordplus/realtime-ui/react';

export function Screens() {
    return <CallScreen rt={useRealtime()} />;
}

<Screens /> sta dentro RealtimeProvider in app/(app)/layout.tsx, quindi una chiamata squilla su qualsiasi pagina un iscritto abbia aperta, in ogni scheda e su ogni dispositivo, e smette di squillare ovunque appena si risponde su uno di essi. La schermata di chiamata è quella di Better Messages, disegnata nell’albero React del sito stesso, così il CSS del sito arriva anche lì.

4. La chiamata nella sua chat

Su un sito di incontri la chiamata sta insieme alla conversazione. La chat sulla pagina di un iscritto accoglie la sua chiamata, accanto ai messaggi:

app/(app)/members/[id]/chat.tsx
<Chat
    rt={useRealtime()}
    room={room}
    to={to}
    name={name}
    avatar={avatar}
    call
    startCall={start}
/>

La sua barra in alto mostra l’altra persona con i suoi pulsanti di chiamata. Il pulsante Video di una scheda del catalogo apre la chat con la chiamata già in partenza (startCall), così l’appuntamento comincia dove i due possono anche scriversi.

La chat è solo uno dei posti in cui può stare una chiamata. Un CallButton ne avvia una da qualsiasi punto, un CallSpot mostra le chiamate nella parte della pagina che scegli tu, e useCalls ti dà ogni chiamata per disegnare la tua schermata.

Cosa è già incluso

  • Una sola richiesta. Il browser chiede una volta sola la fotocamera e il microfono insieme.
  • Si collega sempre. Una chiamata tra due persone va direttamente da un browser all’altro quando può, tramite relay sulle porte che ogni rete lascia aperte quando non può, e tramite media server quando nemmeno quelli riescono ad arrivare, passando dall’uno all’altro senza riagganciare, cifrata su ogni percorso.
  • Resta attiva. La connessione, e con essa la chiamata, resta in piedi mentre l’iscritto si sposta tra le pagine del sito.
  • Chiamate di gruppo con la stessa API. Una stanza con più persone gira sui media server, con un pulsante e una schermata tutti suoi per la chiamata di gruppo (videochiamate per le app).

Provalo, poi costruisci il tuo

Nella demo dal vivo, le undici persone sono solo tue per un giorno, quindi qualunque cosa tu scriva e chiunque tu chiami, nessun altro lo vede. Sotto ogni pagina, How this page works mostra il codice che la fa funzionare, un passo alla volta, con i valori della pagina che hai aperto.

La guida al sito di incontri costruisce l’intero sito, con ogni file della sua parte in tempo reale per intero, e le chiamate in Next.js raccoglie tutti i modi per avviare una chiamata nella tua app.

WordPlus Cloud for Apps ha un prezzo in base alle connessioni simultanee, a partire da 1.000, con fatturazione mensile o annuale. Scopri cosa fa per le app, o i prezzi per le app. Domande sulla tua integrazione? Scrivici.