Next.js MQTT entegrasyonu ilk başta kafa karıştırıcıdır, çünkü Next.js kodunuzu birkaç farklı yerde çalıştırır: tarayıcıda, bir Node.js sunucusunda, serverless fonksiyonlarda ve bazen edge'de. Bu rehberde bu yerlerden hangisinin bir MQTT bağlantısını tutabileceğini öğrenecek ve neredeyse her projeyi kapsayan üç deseni kuracaksınız: güvenli WebSocket üzerinden subscribe olan bir client component, sunucu tarafı kimlik bilgileriyle publish yapan bir route handler ve bir Server-Sent Events köprüsü. Tüm örnekler ConnectMQ'nun broker.connectmq.com üzerindeki WebSocket uç noktasını ve App Router ile MQTT.js v5'i kullanır.
Next.js'te MQTT Nerede Çalışabilir?
MQTT uzun ömürlü bir bağlantıya ihtiyaç duyar. Nereye uyduğunu bu tek gerçek belirler:
| Çalışma ortamı | Uzun ömürlü MQTT bağlantısı? | Aktarım | Tipik kullanım |
|---|---|---|---|
| Tarayıcı (client component) | Evet, sekme açık olduğu sürece | wss:// port 8084, yol /mqtt |
Canlı paneller, cihaz kontrolleri |
Node.js sunucusu (next start, container, VM) |
Evet | mqtts:// 8883 veya wss:// 8084 |
Paylaşılan subscriber, SSE köprüsü, publish |
| Serverless fonksiyon | Hayır, yalnızca istek başına | mqtts:// 8883 |
Tek bir komut publish edip bağlantıyı kesmek |
| Edge runtime | Hayır | MQTT.js TCP için desteklenmez | MQTT için kaçının |
| Server Components | Hayır, bir kez render edilirler | - | Bunun yerine veriyi veritabanından okuyun |
Bir Server Component render edilir ve biter; bir aboneliği canlı tutamaz. Server Actions da istek kapsamlıdır. Dolayısıyla asıl seçim, tarayıcıdan bağlanmak ile Node.js üzerinde çalışan bir sunucu route'undan bağlanmak arasındadır.
İstemci kütüphanesini bir kez kurun:
npm install mqtt
MQTT.js v5 hem Node.js hem de tarayıcı derlemesiyle gelir ve bundler'lar doğru olanı otomatik olarak seçer. Kütüphanenin kendisini MQTT.js Node.js rehberi ele alır; burada Next.js'e nasıl oturduğuna odaklanıyoruz.
Desen 1: Client Component'te WebSocket Üzerinden Subscribe
Canlı paneller için tarayıcı doğrudan broker'a bağlanır. Arada sunucu adımı yoktur, gecikme minimumdur ve Next.js sunucunuz mesaj başına hiçbir iş yapmaz. Tarayıcılar ham TCP açamaz, bu nedenle bu yöntem her zaman güvenli WebSocket üzerinden MQTT kullanır. Aktarım katmanını WebSocket üzerinden MQTT yazımız açıklıyor.
app/hooks/useMqtt.js içinde yeniden kullanılabilir bir hook oluşturun:
"use client";
import { useEffect, useRef, useState } from "react";
import mqtt from "mqtt";
const BROKER_URL = process.env.NEXT_PUBLIC_MQTT_URL ?? "wss://broker.connectmq.com:8084/mqtt";
export function useMqtt(topic) {
const clientRef = useRef(null);
const [status, setStatus] = useState("connecting");
const [messages, setMessages] = useState([]);
useEffect(() => {
const clientId = "next-" + Math.random().toString(16).slice(2, 10);
const client = mqtt.connect(BROKER_URL, {
clientId,
protocolVersion: 5,
clean: true,
keepalive: 30,
reconnectPeriod: 3000,
});
clientRef.current = client;
client.on("connect", () => {
setStatus("connected");
client.subscribe(topic, { qos: 1 });
});
client.on("reconnect", () => setStatus("reconnecting"));
client.on("close", () => setStatus("disconnected"));
client.on("message", (t, payload) => {
setMessages((prev) => [...prev.slice(-99), { topic: t, text: payload.toString(), at: Date.now() }]);
});
return () => {
client.end(true);
clientRef.current = null;
};
}, [topic]);
const publish = (t, payload) => clientRef.current?.publish(t, payload, { qos: 1 });
return { status, messages, publish };
}
Bunu bir sayfada, app/live/page.jsx içinde kullanın:
"use client";
import { useMqtt } from "../hooks/useMqtt";
const PREFIX = "connectmq/demo/next-k7f3q2";
export default function LivePage() {
const { status, messages } = useMqtt(`${PREFIX}/sensors/#`);
return (
<main style={{ padding: 24 }}>
<h1>Live sensors</h1>
<p>Broker: {status}</p>
<ul>
{messages.map((m) => (
<li key={m.at + m.topic}>{m.topic}: {m.text}</li>
))}
</ul>
</main>
);
}
React, JSX içindeki metni escape eder; bu yüzden payload'ları bu şekilde render etmek script enjeksiyonuna karşı güvenlidir. Bir terminalden test ölçümü publish edin ve ekranda belirdiğini izleyin:
mosquitto_pub -h broker.connectmq.com -p 1883 -i next-test-3b1c -t 'connectmq/demo/next-k7f3q2/sensors/room1' -m '{"t":22.4}'
Geliştirme sırasında can yakan üç ayrıntı
- Strict Mode çift mount. Geliştirme ortamında React, cleanup hatalarını ortaya çıkarmak için bileşenleri mount eder, unmount eder ve yeniden mount eder. Effect cleanup içinde
client.end()yoksa iki bağlantı elde edersiniz. Client ID'yi effect içinde üretmek de iki örneğin aynı ID için çekişmesini önler. - Asla modül kapsamında bağlanmayın. Bir client dosyasındaki üst düzey
mqtt.connect()çağrısı, o bileşenin sunucu tarafı render'ı sırasında da çalışır ve sunucunuzda bir TCP bağlantısı açar. Bağlantıları yalnızca tarayıcıda çalışanuseEffectiçinde tutun. NEXT_PUBLIC_herkese açık demektir. Bu öneke sahip her değişken JavaScript bundle'ına gömülür. Oraya yalnızca broker URL'sini koyun, asla bir parola koymayın.
Desen 2: Özel Kimlik Bilgileriyle Route Handler'dan Publish
Bir vanayı açmak veya bir cihazı yeniden başlatmak gibi fiziksel dünyayı değiştiren komutlar, her ziyaretçinin okuyabileceği kimlik bilgileriyle gönderilmemelidir. Bunun yerine tarayıcı API'nizi çağırır, sunucu kullanıcıyı doğrular ve ortam değişkenlerinde tutulan kimlik bilgileriyle publish yapar.
Önce lib/mqtt-server.js içinde sunucu tarafı bir singleton oluşturun. İstemciyi globalThis üzerinde önbelleğe almak, geliştirme sırasında hot reload'un her dosya kaydında yeni bir bağlantı oluşturmasını engeller:
import mqtt from "mqtt";
const globalForMqtt = globalThis;
export function getMqttClient() {
if (!globalForMqtt.__mqttClient) {
globalForMqtt.__mqttClient = mqtt.connect(process.env.MQTT_URL ?? "mqtts://broker.connectmq.com:8883", {
clientId: process.env.MQTT_CLIENT_ID ?? "next-server-" + Math.random().toString(16).slice(2, 10),
username: process.env.MQTT_USERNAME,
password: process.env.MQTT_PASSWORD,
protocolVersion: 5,
clean: true,
reconnectPeriod: 5000,
});
}
return globalForMqtt.__mqttClient;
}
Ardından app/api/command/route.js içindeki route handler:
import { getMqttClient } from "@/lib/mqtt-server";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
const PREFIX = "connectmq/demo/next-k7f3q2";
const ALLOWED = new Set(["on", "off"]);
export async function POST(request) {
const { deviceId, action } = await request.json();
if (!/^[a-z0-9-]{1,32}$/.test(deviceId ?? "") || !ALLOWED.has(action)) {
return Response.json({ error: "invalid command" }, { status: 400 });
}
// Publish etmeden önce kullanıcının oturumunu veya rolünü burada kontrol edin.
await getMqttClient().publishAsync(
`${PREFIX}/devices/${deviceId}/cmd`,
JSON.stringify({ action, at: new Date().toISOString() }),
{ qos: 1 }
);
return Response.json({ ok: true });
}
Özel bir ConnectMQ broker'ı için .env.local dosyanız şöyle görünür:
MQTT_URL=mqtts://YOUR_BROKER_HOST:8883
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
MQTT_CLIENT_ID=YOUR_CLIENT_ID
deviceId değerini katı bir desene göre doğrulamak isteğe bağlı değildir. Kullanıcı girdisinin doğrudan bir topic'e akmasına izin verirseniz, +, # veya fazladan eğik çizgi içeren bir istek hiç amaçlamadığınız topic'lere publish yapabilir.
Serverless platformlarda
Serverless barındırmada bir fonksiyon örneği istekler arasında dondurulabilir veya atılabilir; bu yüzden önbelleğe alınmış bir bağlantıya güvenilemez. Bunun yerine aynı istek içinde bağlanın, publish edin ve kapatın:
import mqtt from "mqtt";
export const runtime = "nodejs";
export async function POST(request) {
const { message } = await request.json();
const client = await mqtt.connectAsync(process.env.MQTT_URL, {
clientId: "next-fn-" + Math.random().toString(16).slice(2, 10),
username: process.env.MQTT_USERNAME,
password: process.env.MQTT_PASSWORD,
});
try {
await client.publishAsync("connectmq/demo/next-k7f3q2/notices", String(message), { qos: 1 });
} finally {
await client.endAsync();
}
return Response.json({ ok: true });
}
Bu, her isteğe bir TLS el sıkışması ekler; ara sıra gönderilen komutlar için sorun değildir ancak yüksek frekanslı publish için uygun değildir. ConnectMQ'nun özel broker'larda client ID'leri verilen kimlik bilgilerine bağladığını unutmayın; çok sayıda kısa ömürlü fonksiyonu eş zamanlı çalıştırıyorsanız, kimlik bilgilerinizin ve client ID'lerinizin portalda nasıl sağlandığını kontrol edin.
Desen 3: Server-Sent Events ile MQTT'yi Tarayıcıya Aktarmak
Bazen tarayıcının broker ile hiç konuşmasını istemezsiniz; örneğin topic verisinin kullanıcı başına filtrelenmesi gerektiğinde. Bir Node.js route'u tek bir abonelik tutabilir ve mesajları Server-Sent Events (SSE) üzerinden tarayıcılara dağıtabilir. Bu yalnızca uzun süre çalışan bir Node.js sunucusunda işe yarar, kısa zaman aşımlı serverless fonksiyonlarda çalışmaz.
Singleton içinde bir kez subscribe olun (bunu getMqttClient içinde istemciyi oluşturduktan sonra ekleyin):
globalForMqtt.__mqttClient.on("connect", () => {
globalForMqtt.__mqttClient.subscribe("connectmq/demo/next-k7f3q2/sensors/#", { qos: 1 });
});
Ardından app/api/stream/route.js dosyasını ekleyin:
import { getMqttClient } from "@/lib/mqtt-server";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export async function GET(request) {
const client = getMqttClient();
const encoder = new TextEncoder();
const stream = new ReadableStream({
start(controller) {
const onMessage = (topic, payload) => {
const data = JSON.stringify({ topic, payload: payload.toString() });
controller.enqueue(encoder.encode(`data: ${data}\n\n`));
};
client.on("message", onMessage);
request.signal.addEventListener("abort", () => {
client.off("message", onMessage);
controller.close();
});
},
});
return new Response(stream, {
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" },
});
}
Bir client component içinde new EventSource("/api/stream") her mesajı bir olay olarak alır. Artık tek bir broker bağlantısı tüm izleyicilere hizmet eder ve bu da sizi bağlantı limitlerinin rahatça içinde tutar.
Next.js MQTT Bağlantı Sorunlarını Giderme
MQTT'yi bir Next.js projesine bağlarken bazı sorunlar tekrar tekrar karşınıza çıkar:
- Tarayıcı konsolunda "WebSocket connection failed". URL'yi dikkatle kontrol edin:
wss://olmalı, port 8084 olmalı ve/mqttile bitmelidir. HTTPS üzerinden sunulan bir sayfa düzws://kullanamaz. - Derleme sırasında "Module not found: net" veya "tls". Bir şey, genellikle Node.js modüllerini de import eden paylaşılan bir dosya aracılığıyla, yalnızca sunucuya ait kodu client bundle'ına çekmiştir.
lib/mqtt-server.jsdosyasını"use client"ile işaretlenmiş hiçbir şeyin içine almayın. - Route handler takılıyor, sonra zaman aşımına uğruyor. Broker girişi reddetmiştir veya port sunucunuzdan engellenmiştir; bu yüzden
publishAsynchiç gelmeyecek bir bağlantıyı bekler.errorolayını dinleyip loglayın ve aynı kimlik bilgilerini sunucudanmosquitto_pubile test edin. - Mesajlar sunucuda iki kez geliyor. Singleton
globalThisüzerinde önbelleğe alınmadığı için hot reload ikinci bir istemci oluşturmuştur. Düzelttikten sonra geliştirme sunucusunu yeniden başlatın.
Doğru Deseni Seçmek
Karar vermek için bu kontrol listesini kullanın:
- Salt okunur canlı veri, herkese açık veya düşük hassasiyetli: Desen 1, salt okunur kimlik bilgileriyle WebSocket üzerinden tarayıcı.
- Yan etkisi olan eylemler: Desen 2, sunucu tarafı kimlik bilgileri ve girdi doğrulaması içeren route handler.
- Kullanıcı başına filtreleme veya küçük bir planda çok sayıda izleyici: Desen 3, Node.js sunucusunda SSE köprüsü.
- Geçmiş verisi grafikleri: Yukarıdakilerin hiçbiri tek başına yetmez. Mesajları ayrı bir subscriber ile saklayın ve veritabanını bir Server Component'ten sorgulayın.
Çoğu üretim uygulaması 1 ve 2'yi birleştirir: tarayıcı canlı güncellemeler için subscribe olur ve her komut bir route handler üzerinden geçer. Bileşen düzeyindeki ayrıntılar için React MQTT eğitimine, tipli payload'lar için TypeScript ile MQTT rehberine göz atın. MQTT sohbet uygulaması eğitimi tamamen tarayıcı tarafında çalışan bir tasarımın iyi bir örneğidir; yığınınızın bir kısmı başka bir dildeyse MQTT istemci kütüphanelerine genel bakış yardımcı olur. Resmi Next.js route handler dokümantasyonu çalışma ortamı seçeneklerini derinlemesine ele alır.
Next.js Uygulamanızı ConnectMQ'ya Bağlayın
ConnectMQ, client component'ler için 8084 portunda güvenli WebSocket ve route handler'lar için 8883 portunda TLS sunan tam yönetilen bir MQTT hizmetidir; böylece bir Next.js uygulamasının her iki tarafı da aynı broker ile konuşur. Prototipinizi herkese açık MQTT test broker'ı üzerinde geliştirin, ardından özel bir bulut MQTT broker'ına geçin. Serverless planında ücretsiz kaydolun, kredi kartı gerekmez.
Sık Sorulan Sorular
Next.js Server Components içinde MQTT kullanabilir miyim?
Canlı veri için hayır. Server Components istek başına bir kez render edilir ve bir aboneliği tutamaz. Bir client component'te veya Node.js route handler'ında subscribe olun, saklanan veriyi okumak için de Server Components kullanın.
MQTT, Next.js Edge runtime üzerinde çalışır mı?
Edge runtime, MQTT.js'in TCP ve TLS için kullandığı Node.js ağ API'lerini sağlamaz. Broker ile konuşan her route'ta export const runtime = "nodejs" ayarlayın.
Next.js'te MQTT kimlik bilgilerini tarayıcıdan nasıl uzak tutarım?
Bunları herkese açık olmayan ortam değişkenlerinde saklayın ve yalnızca route handler'larda veya sunucu kodunda kullanın. NEXT_PUBLIC_ önekli her şey client bundle'ına girer.
Geliştirme sırasında neden iki MQTT bağlantısı görüyorum?
React Strict Mode geliştirme ortamında bileşenleri iki kez mount eder ve Next.js hot reload sunucu modüllerini yeniden çalıştırır. İstemciyi effect cleanup içinde kapatın ve sunucu istemcilerini globalThis üzerinde önbelleğe alın.



