So bauen Sie private Videosprechstunden in Next.js

Eine Videosprechstunde sieht aus wie ein einzelner Anruf, doch in einer Next.js-App sind es mehrere Features. Jemand bucht eine halbe Stunde bei einer Ärztin, einem Anwalt, einer Nachhilfe oder einer Beratung, und dieser Termin verschwindet sofort von jeder Seite, damit niemand sonst ihn bucht. Zur vereinbarten Zeit wartet die Klientin oder der Klient, und die Fachperson sieht das auf jeder Seite, auf der sie gerade ist, und lässt die Person herein. Der Termin ist ein Videoanruf der beiden, dem sonst niemand beitreten darf und den der Dienst, der ihn überträgt, weder sehen noch hören kann. Daneben die Nachrichten und Dateien des Termins, gespeichert auf Ihrem eigenen Server, und danach die Notizen der Fachperson.
Dieser Beitrag geht jeden Teil durch, den eine echte App braucht, mit einem vollständigen, funktionierenden Beispiel: Consultations, die Website für private Sprechstunden aus unserer Next.js-Anleitung, auf der Klientinnen und Klienten Fachleute buchen, im Wartezimmer warten und sie per Video treffen. Sie können es live ausprobieren: Seien Sie Ava, mit einem Termin bei Lena, der jetzt offen ist, und klicken Sie auf I'm ready. Öffnen Sie dann die Website in einem privaten Fenster als Lena, sehen Sie Ava im Wartezimmer, und lassen Sie Ava herein.
Was eine Videosprechstunde braucht
- Eine Buchung, die hält. Die noch freien Zeiten auf jeder Seite, und eine Zeit, die von allen Seiten verschwindet, sobald jemand sie bucht.
- Ein Wartezimmer. Die Klientin oder der Klient sagt, dass alles bereit ist, und die Fachperson sieht das Warten auf jeder Seite, nicht nur auf der des Termins.
- Hereinlassen. Nur die beiden des Termins, nur rund um seine Zeit, und die Person, die gebucht hat, erst, wenn die Fachperson sie hereinlässt.
- Vertraulichkeit. Kamera und Mikrofon Ende-zu-Ende-verschlüsselt, sodass die Server, die den Anruf übertragen, weiterleiten, was sie weder sehen noch hören können.
- Nachrichten und Dateien daneben, gespeichert dort, wo Sie Ihre Daten haben, und danach Notizen für die Person, die gebucht hat.
- Ein Ende. Der Termin schließt am Ende seiner halben Stunde, und niemand kommt wieder hinein.
Sie können all das selbst bauen, mit WebRTC, Medienservern und einem eigenen Websocket-Server. Oder Sie überlassen einer Echtzeit-Cloud die Medien, das Wartezimmer und den Anrufbildschirm, während Ihr Server behält, was wirklich Ihres ist: die Buchungen, die Regeln und die Dateien. Genau das macht Consultations, über WordPlus Cloud.
Das Beispiel: eine Website für Sprechstunden
Fachleute bieten halbe Stunden an: eine Hausärztin, ein Anwalt, eine Nachhilfe, eine Beratung. Klientinnen und Klienten buchen eine Zeit oder sprechen sofort mit einer Fachperson, die gerade verfügbar ist. Jeder Termin hat eine eigene Seite: den Videotermin, seine Nachrichten und, sobald er vorbei ist, die Notizen der Fachperson. Es ist eine Next.js-App mit App Router, Better Auth und Postgres, und ihr Echtzeit-Teil besteht aus wenigen Dateien. Fünf Schritte machen ihre Termine.
1. Zeiten, die verschwinden, sobald sie gebucht sind
Gebucht wird mit einer Server Action. Der Server speichert die Buchung, und eine halbe Stunde, die schon vergeben ist, lehnt die Datenbank selbst ab. Dann sagt er es über WordPlus Cloud weiter, zweimal:
export async function bookVisit(expert: string, startsAt: number, topic: string): Promise<{ error: string }> {
const me = await currentUserId();
if (!me) return { error: 'Sign in first.' };
const booked = await book(me, expert, startsAt, topic);
if (typeof booked === 'string') return { error: booked };
await realtime.toUser(expert).emit('booked', { appointment: booked.id, name: booked.clientName, startsAt: booked.startsAt });
// Every page showing this expert's times takes this one away.
await realtime.to('experts').emit('changed');
redirect(`/appointments/${booked.id}`);
}toUser() erreicht jede Seite, die die Fachperson offen hat, auf jedem Gerät, mit einem Hinweis auf den neuen Termin. experts ist ein Raum, dem jede angemeldete Seite beitritt und in dem nur der Server spricht. Jede Seite hört changed und fragt ihren Server erneut nach sich selbst, ohne Neuladen:
useRoom('experts', { changed: () => router.refresh() });Wer gerade die Zeiten dieser Fachperson angesehen hat, sieht die halbe Stunde verschwinden. Derselbe Raum meldet den Seiten, wenn ein Termin beginnt oder endet, sodass auch stimmt, welche Fachleute gerade verfügbar sind. WordPlus Cloud weiß, welche Termine laufen (realtime.listCalls()), und die Website führt keine Tabelle dafür.
2. Das Wartezimmer
Jeder Termin hat ein Wartezimmer, einen Presence-Raum, benannt nach dem Termin:
realtime.room('waiting:{id}', { presence: true }, async (user, { id }) => mayChat(user, id));Nur die beiden des Termins kommen hinein. Sobald die Person, die gebucht hat, auf I'm ready klickt, tritt ihre Seite dem Raum bei und bleibt darin, solange die Seite offen ist. Die Seite der Fachperson liest, wer darin ist:
function useWaiting(id: string, client: string): boolean {
return usePresence(`waiting:${id}`).some((member) => String(member.id) === client);
}Presence folgt der Seite: Wer den Tab schließt, verlässt damit allein schon das Wartezimmer. Und die Fachperson muss nicht auf der Seite des Termins sein. Der Server meldet es mit einem waiting-Ereignis über realtime.toUser(), und auf jeder Seite erscheint ein Hinweis „Ava is in the waiting room“, der den Termin öffnet.
3. Hereinlassen
Der Termin ist ein Gruppenanruf, benannt nach dem Termin, visit:81, einmal deklariert, nach diesem Muster, mit seiner Regel:
realtime.groupCalls('visit:{id}', async (user, { id }) => mayJoinVisit(user, id), { encrypt: true });export async function mayJoinVisit(user: string | number | null, id: string): Promise<boolean> {
const appointment = await appointmentOf(id);
if (!appointment || !isParty(appointment, user) || !visitOpen(appointment)) return false;
return String(user) === appointment.expert || appointment.admittedAt !== null;
}Die beiden des Termins und sonst niemand, solange der Termin offen ist: von zehn Minuten vor seiner Zeit bis zum Ende seiner halben Stunde, außer er endete früher. Die Fachperson kommt herein, wann immer er offen ist, die Person, die gebucht hat, erst, nachdem die Fachperson sie hereingelassen hat. Hereinlassen ist eine Server Action, die den Termin markiert und es der Person mitteilt:
export async function letIn(id: string): Promise<boolean> {
const me = await currentUserId();
const appointment = me ? await admit(id, me) : null;
if (!appointment) return false;
await realtime.toUser(appointment.client).emit('admitted', { appointment: id, name: appointment.expertName });
return true;
}Ab dann sagt die Regel Ja, und die wartende Seite kommt von selbst in den Termin. Will eine Seite beitreten, fragt sie zuerst den eigenen Server der Website, unter /api/realtime/calls. Es ist derselbe Route Handler, über den auch die Anrufe der Dating-Website laufen (so implementieren Sie Videoanrufe in Next.js). Die Regel läuft mit der eigenen Sitzung der Website, und die Seite bekommt einen Pass für den Termin, signiert mit dem Secret der App, oder den Grund, warum nicht.
4. Der Termin, Ende-zu-Ende-verschlüsselt
{ encrypt: true } in der Regel macht den Termin Ende-zu-Ende-verschlüsselt. Jeder Browser verschlüsselt Kamera und Mikrofon mit einem Schlüssel, den der Server der Website erzeugt und den nur bekommt, wen die Regel hereinlässt. Die Schlüssel bleiben bei der Website und den beiden Browsern, und die Medienserver von WordPlus Cloud leiten weiter, was sie weder sehen noch hören können (wie Medienserver funktionieren).
Der Termin erscheint in der Seite des Termins selbst, dort, wo die Seite ihn platziert:
<CallSpot rt={rt} call={`visit:${id}`} join={join} joinWith="microphone camera" className="h-[32rem] rounded-2xl">
{/* The waiting room as each of the two sees it, until the page joins */}
</CallSpot>Vor dem Termin zeigt CallSpot, was darin steht: das Wartezimmer, wie jede der beiden Seiten es sieht, mit Let Ava in für die Fachperson. join holt die Seite in den Termin, sobald die Fachperson hereinlässt oder die wartende Person hereingelassen wird. joinWith="microphone camera" schaltet beim Eintreten beides ein, denn bei einem Termin will man sich sofort sehen und hören. Auf einer anderen Seite der App läuft der Termin in einem Fenster in der Ecke weiter und kehrt hierher zurück, sobald die Seite des Termins wieder offen ist.
Ein Termin endet auch. Die Fachperson beendet ihn mit einem Button, und die Website beendet ihn am Ende seiner halben Stunde, mit einem Timer, den sie beim Beginn des Termins stellt:
export async function endVisit(appointment: Appointment) {
await realtime.call(visitOf(appointment.id)).end().catch(() => {}); // not on, if nobody came in
await markEnded(appointment.id);
await realtime.toUser([appointment.expert, appointment.client]).emit('ended', { appointment: appointment.id });
await realtime.to('experts').emit('changed');
}end() nimmt beide aus dem Anruf, die Regel sieht den beendeten Termin und lässt niemanden mehr herein, und beide Seiten zeigen, dass der Termin vorbei ist.
5. Nachrichten und Dateien, dann die Notizen
Jeder Termin hat eine Unterhaltung, den Raum chat:{id}, nur für seine beiden, vor dem Termin, währenddessen und danach. Der fertige Chat zeigt sie neben dem Termin, mit Tippanzeige, Lesebestätigungen und Dateien:
<Chat
rt={useRealtime()}
room={room}
messages={`/api/messages?room=${room}`}
upload="/api/files"
accept="image/*,.pdf,.doc,.docx,.txt"
maxFileSize={10 * 1024 * 1024}
/>Ein Foto, ein Scan oder ein Vertrag geht beim Auswählen an die eigene Route der Website, und die Route fragt die Regel des Raums, bevor sie die Datei speichert:
if (!me || !appointment || !(file instanceof File) || !(await realtime.mayJoin(room, me))) {
return Response.json({ error: 'This file is not for this conversation.' }, { status: 403 });
}Die Website speichert die Datei in ihrer eigenen Datenbank und öffnet sie nur den beiden. WordPlus Cloud überträgt nur die Nachricht, die sagt, wo sie liegt, versiegelt mit einem Schlüssel, den die Website hält, und niemals die Datei selbst. Auch jede Nachricht speichert die Website, aus dem Webhook message, sodass die Unterhaltung nach dem Termin für beide da ist. Ist der Termin vorbei, schreibt die Fachperson Notizen für die Person, die gebucht hat. Die Website speichert sie, und deren Seite zeigt sie sofort, wieder über toUser().
Was dazugehört
- Jetzt sprechen. Wer Rat sucht, fragt eine gerade verfügbare Fachperson nach einem Gespräch sofort. Die Seite der Fachperson zeigt die Anfrage, mit einer Minute Zeit zum Annehmen, und beide gehen direkt in einen neuen Termin.
- Wer verfügbar ist, aktuell auf jeder Seite. Eine Fachperson in einem Termin erscheint als beschäftigt, aus
realtime.listCalls(), ohne eigene Tabelle. - Online oder nicht. Das Wartezimmer zeigt, ob die Fachperson schon online ist, aus
useOnline(). - Namen und Gesichter aus Ihrer App. Jede Kachel und jede Nachricht zeigt Namen und Foto so, wie das
profileder Website sie liefert, ohne Benutzerliste in WordPlus Cloud.
Ausprobieren, dann selbst bauen
In der Live-Demo gehören die Fachleute, Klientinnen und Klienten einen Tag lang nur Ihnen: Was Sie dort schreiben oder mit wem Sie sich treffen, sieht sonst niemand, und ein Termin endet nach 10 Minuten von selbst. Unter jeder Seite zeigt How this page works Schritt für Schritt den Code, der sie erzeugt, mit den Werten der Seite, die Sie gerade geöffnet haben.
Die Anleitung für private Sprechstunden baut die ganze Website auf, mit jeder Datei ihres Echtzeit-Teils, Anrufe in Next.js zeigt jeden Weg, einen Anruf in Ihre eigene App zu bringen, und Verschlüsselung erklärt, was versiegelt ist und wer welchen Schlüssel hält. Für ein Meeting eines ganzen Teams statt zweier Personen lesen Sie So implementieren Sie Videokonferenzen in Next.js.
Für WordPlus Cloud for Apps gelten Preise nach gleichzeitigen Verbindungen, ab 1.000, monatlich oder jährlich abgerechnet. Ein Termin ist ein Gruppenanruf zweier Personen. Mit 1.000 Verbindungen sind Gruppenanrufe bis zu 32 Personen mit Video gleichzeitig ohne Abrechnung, also 16 Termine, wobei eine Person nur mit Audio halb zählt, und mit jeweils 1.000 weiteren Verbindungen kommen 12 hinzu. Darüber verbraucht jede weitere Person mit Video ein Credit pro Minute, und Anrufe zwischen zwei Personen verbrauchen keine. Sehen Sie sich Videoanrufe für Apps an, oder werfen Sie einen Blick auf die Preise für Apps. Fragen zu Ihrer Integration? Schreiben Sie uns.