Come costruire una community online in Next.js

Un forum conserva ciò che i suoi membri hanno scritto. Una community che sembra viva offre più cose nello stesso momento. I membri condividono una foto e ricevono una risposta mentre sono ancora lì, entrano in una stanza per parlare, con la voce o in video, guardano gli eventi del club in diretta e salgono sul palco con il proprio lavoro, e vedono chi c’è in questo momento. In un’app Next.js, ognuna di queste cose è una connessione che resta aperta, con molte persone insieme.
Questo articolo passa in rassegna ogni parte che serve a una vera app per una community, con un esempio completo e funzionante: Community, il club di fotografia della nostra guida Next.js, con i suoi canali, le sue stanze in cui fare un salto e i suoi eventi dal vivo. Puoi provarlo dal vivo: entra come Mia, host del club, la cui revisione dei portfolio inizia adesso, apri lo studio e vai in diretta. Poi apri il club in una finestra privata come Ava, guarda la revisione, alza la mano e sali sul palco.
Cosa serve a una community online
- Canali al passo. Messaggi e foto arrivano subito a tutti quelli che stanno leggendo, con chi sta scrivendo e le reazioni, e restano salvati per chi arriva dopo. Le foto si aprono solo per i membri.
- Stanze in cui fare un salto. Stanze vocali, e stanze con fotocamere e schermi, in cui i membri entrano ed escono quando vogliono, molte persone insieme grazie ai server media (come funzionano i server media). Host che possono silenziare qualcuno o farlo uscire.
- Eventi dal vivo. Un host per tutti i membri, con una chat e le reazioni accanto all’immagine, e un membro invitato sul palco a mostrare il proprio lavoro (come funziona il live streaming in Next.js).
- Cosa succede adesso. Chi è in quale stanza e quale evento è in diretta, aggiornato su ogni pagina, senza ricaricare.
- Chi c’è. Chi è online, e un modo per scrivere o chiamare ciascuno.
Tutto questo passa da connessioni che restano aperte finché un membro è sul sito, cosa che una funzione serverless non può tenere (perché, e cosa fare). Puoi costruire ogni parte da solo, su websocket, WebRTC e server media tuoi. Oppure lasci che un cloud realtime gestisca le connessioni, le chiamate e le dirette, mentre il tuo server tiene ciò che è davvero tuo: i membri, le regole e i dati. È ciò che fa il club, con WordPlus Cloud.
L’esempio: un club di fotografia
I membri condividono foto nei canali per avere un parere, come #critique e #trips. Fanno un salto nelle sue stanze: Coffee lounge con la voce, Editing hangout con fotocamere e schermi. Gli host conducono eventi dal vivo, una revisione dei portfolio o un corso di fotografia notturna, in cui i membri salgono sul palco. È un’app Next.js con l’App Router, Better Auth e Postgres, e la sua parte in tempo reale sta in pochi file. Cinque passi la costruiscono.
1. Canali con foto, salvati sul tuo server
Ogni canale è una stanza che prende il nome dal suo numero: channel:2. Il sito li dichiara una volta sola, con quel modello, insieme alla regola su chi può starci:
const MESSAGES = { history: 50, emit: ['message', 'typing', 'reaction', 'read'], webhook: ['message', 'reaction'] };
realtime.room('channel:{id}', MESSAGES, async (user, _params, room) => mayWrite(user, room));Qualunque membro può stare in un canale, e nessun altro. Messaggi, indicazione di chi scrive, reazioni e conferme di lettura passano da pagina a pagina attraverso WordPlus Cloud, sigillati, e i webhook message e reaction portano ciascuno al server del sito, che lo salva nel proprio database. La pagina del canale è la chat già pronta su quella stanza:
<Chat
rt={useRealtime()}
room={room}
people={people}
messages={`/api/messages?room=${room}`}
upload="/api/files"
accept="image/jpeg,image/png,image/webp,image/gif"
maxFileSize={15 * 1024 * 1024}
/>messages è la rotta del sito per i messaggi più vecchi, caricati man mano che un membro scorre verso l’alto. upload dà al campo di scrittura la sua graffetta, e accetta anche foto incollate o trascinate sulla chat: ognuna va alla rotta del sito appena viene scelta, e il messaggio porta solo dove si apre. WordPlus Cloud non vede mai la foto. La rotta decide con la regola della stanza stessa:
if (!me || !(file instanceof File) || !/^(channel|chat):\d+$/.test(room) || !(await realtime.mayJoin(room, me))) {
return Response.json({ error: 'This photo is not for this conversation.' }, { status: 403 });
}realtime.mayJoin() esegue la stessa regola dell’ingresso nella stanza, così chi può leggere un canale può condividerci una foto, e nessun altro. Ogni foto si apre a un indirizzo tutto suo, che controlla di nuovo chi la apre: chi è fuori dal club non riceve nulla, qualunque indirizzo abbia.
2. Stanze vocali di cui si occupano gli host
Una stanza è una chiamata di gruppo che prende il nome dal suo numero, lounge:3, in cui qualunque membro può fare un salto. La regola fa degli host del club i suoi host:
realtime.groupCalls('lounge:{id}', async (user) => {
if (!(await isMember(user))) return false;
return (await isHost(user)) ? { admin: true } : true;
});La stanza compare nella sua pagina, con dentro il pulsante per entrare:
<CallSpot rt={rt} call={room} className="h-[calc(100dvh-14rem)] rounded-2xl">
<GroupCallButton rt={rt} room={room} type={kind} title={name}>
{people.length > 0 ? 'Drop in' : `Open ${name}`}
</GroupCallButton>
</CallSpot>type è audio per una stanza vocale e video per una con fotocamere. Prima che un membro entri, CallSpot mostra ciò che contiene: chi è nella stanza adesso, e il pulsante. Una volta dentro, la chiamata prende il suo posto: l’immagine di tutti, o le loro fotocamere e i loro schermi, e i controlli. Un membro entra con microfono e fotocamera spenti e li accende dalla chiamata, così fare un salto non apre mai un microfono di sorpresa. Su un’altra pagina del sito, la stanza continua in una finestra nell’angolo, e torna quando la sua pagina si riapre.
admin: true è tutto ciò che serve agli host. Sullo schermo di un host, ogni riquadro degli altri ha silenzia e rimuovi, che la schermata di chiamata chiede attraverso la stessa rotta, la quale li accetta solo da un host.
3. Eventi dal vivo, con i membri sul palco
Un evento è una diretta che prende il nome dal suo numero: event:5. La sua regola dice chi è chi: l’host dell’evento va in diretta, e ogni altro membro guarda:
realtime.streams(
'event:{id}',
async (user, { id }) => {
const event = await eventOf(id);
if (!event || !(await isMember(user))) return false;
return String(user) === event.host ? { role: 'host', quality: '1080p' } : 'viewer';
},
{ stage: true, reactions: true },
);L’host va in diretta da uno studio tutto suo, una pagina del sito con l’immagine grande, Go live sotto, e accanto la chat e le mani alzate:
<LiveStream
name={`event:${event.id}`}
view="studio-page"
title={event.title}
url={`/events/${event.id}`}
backUrl={`/events/${event.id}`}
/>La pagina dell’evento dà a ogni membro il player con la sua chat, lo stesso <LiveStream> con lo stesso nome. Un host può anche andare in diretta da OBS, con l’indirizzo e la chiave che dà lo studio. stage: true è il palco: un membro alza la mano dal player, l’host la vede sotto Guests e lo invita, e appena preme Join stage, la sua fotocamera e il suo microfono vanno in diretta accanto all’host, per tutti quelli che guardano.
4. Cosa succede adesso, su ogni pagina
Accanto a ogni pagina, una barra laterale mostra chi è in ciascuna stanza e quale evento è in diretta, con quanti lo guardano. Il sito non tiene nessuna tabella di tutto questo. Lo sa WordPlus Cloud:
export async function liveNow(): Promise<Map<string, number>> {
const live = new Map<string, number>();
for (const stream of await realtime.listStreams()) {
const event = idIn(stream.name, 'event');
if (event) live.set(event, stream.viewers);
}
return live;
}realtime.listCalls() e realtime.call(name).get() dicono lo stesso delle stanze e di chi c’è in ciascuna. Per tenerlo aggiornato, WordPlus Cloud manda al sito un webhook ogni volta che qualcuno entra in una stanza o ne esce, o un evento va in diretta o finisce. Il sito lo dice in una stanza a cui si unisce ogni pagina di un membro, e quando un evento va in diretta, lo dice a ogni membro:
realtime.on('stream.live', async ({ stream }) => {
const id = idIn(stream, 'event');
const event = id ? await eventOf(id) : null;
if (!event) return;
await realtime.to('club').emit('changed');
const everyone = (await members()).map((person) => person.id).filter((person) => person !== event.host);
await realtime.toUser(everyone).emit('live', { event: event.id, title: event.title, host: event.hostName });
});Nel layout, ogni pagina sente entrambe le cose:
useRoom('club', { changed: () => router.refresh() });
useUserEvent('live', (data: { event: string; title: string; host: string }) => {
if (path !== `/events/${data.event}`) show({ text: `${data.host} is live: ${data.title}`, href: `/events/${data.event}`, live: true });
});La prima fa sì che ogni pagina si richieda al server, senza ricaricare, così la barra laterale e la pagina aperta restano aggiornate. La seconda mostra una notifica su qualunque pagina si trovi un membro: «Mia is live: Live portfolio review», che apre l’evento. group.joined, group.left e stream.ended dicono changed nella stessa stanza.
5. Chi è online
WordPlus Cloud sa chi è online: ogni pagina di cui il sito ha garantito il membro rende online quel membro, e la pagina sa chi arriva e chi se ne va, senza alcuna richiesta al server del sito:
const { online } = useOnline(people.map((person) => person.id));
const here = people.filter((person) => online[person.id]);
const away = people.filter((person) => !online[person.id]);L’elenco dei membri mette per primi quelli online, ciascuno con Message e Call, e accanto a ogni pagina il layout elenca il club allo stesso modo, con gli host segnati. La pagina di un membro dice se è online, o quando è stato visto l’ultima volta.
Cosa è incluso
- Conversazioni a due. La pagina di un membro contiene la tua conversazione con quella persona, foto comprese, e la vostra chiamata compare accanto ai messaggi.
- Reazioni e una chat su ogni evento, accanto all’immagine, e la diretta da OBS o da qualunque encoder che trasmette via WHIP.
- La condivisione dello schermo in una stanza con fotocamere, dai controlli della chiamata.
- Nomi e volti dalla tua app. Ogni riquadro, messaggio e palco mostra il nome e la foto di un membro così come li dà il
profiledel sito, senza alcun elenco di utenti tenuto in WordPlus Cloud. - Togliere l’accesso. Quando qualcuno lascia il club, da lì in poi le regole dicono di no, e
realtime.call(name).remove(user)lo fa uscire subito da una stanza.
Provalo, poi costruisci il tuo
Nella demo dal vivo, il club è solo tuo per un giorno, quindi qualunque cosa tu condivida e chiunque tu incontri, nessun altro lo vede, e un evento o una stanza finisce da solo dopo 10 minuti. 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 community costruisce l’intero sito, con ogni file della sua parte in tempo reale per intero. Le dirette in Next.js e le chiamate in Next.js raccolgono tutti i modi per mettere una diretta o una chiamata nella tua app. Per un team invece di un club, la videoconferenza in Next.js costruisce le riunioni di un’intranet per team.
WordPlus Cloud for Apps ha un prezzo in base alle connessioni simultanee, a partire da 1.000, con fatturazione mensile o annuale. Con 1.000 connessioni, le dirette sono senza conteggio fino a 50 spettatori contemporanei, e le chiamate di gruppo fino a 32 persone contemporanee in video, dove una persona in audio conta la metà. Ogni 1.000 connessioni in più aggiungono 20 spettatori e 12 persone in video. Oltre, ogni spettatore o persona in video in più consuma un credito al minuto, una diretta conteggiata solo mentre è in onda, e le chiamate tra due persone non ne consumano. Scopri il live streaming per le app, le videochiamate per le app, o i prezzi per le app. Domande sulla tua integrazione? Scrivici.