<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: omaxtech</title>
    <description>The latest articles on DEV Community by omaxtech (@omaxtech2025).</description>
    <link>https://dev.to/omaxtech2025</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4171446%2Ff15bd8d4-f12e-45fd-8d1a-7c78422da658.jpg</url>
      <title>DEV Community: omaxtech</title>
      <link>https://dev.to/omaxtech2025</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/omaxtech2025"/>
    <language>en</language>
    <item>
      <title>How to Add LiveKit Video Calling to a Next.js App</title>
      <dc:creator>omaxtech</dc:creator>
      <pubDate>Tue, 13 Oct 2026 15:51:00 +0000</pubDate>
      <link>https://dev.to/omaxtech2025/how-to-add-livekit-video-calling-to-a-nextjs-app-3nf7</link>
      <guid>https://dev.to/omaxtech2025/how-to-add-livekit-video-calling-to-a-nextjs-app-3nf7</guid>
      <description>&lt;h2&gt;
  
  
  Building Embedded Video &amp;amp; Audio Calling with LiveKit Cloud, Next.js, and Laravel
&lt;/h2&gt;

&lt;p&gt;We needed video and audio calls directly inside the product—not another third-party Zoom link. This is how we compared LiveKit with Twilio, Daily, Agora, and Zoom, and then wired LiveKit Cloud into a Next.js client and a Laravel API.&lt;/p&gt;

&lt;p&gt;Most products do not have a video problem. They have a meeting-link problem.&lt;/p&gt;

&lt;p&gt;A customer books a session in your app. You paste a Zoom or Google Meet URL into an email. They leave your product, wait in someone else’s lobby, and when the call ends, you still do not own the recording, the attendance data, or the next click. That is fine for an internal standup. It is a leak if calling is part of the core product.&lt;/p&gt;

&lt;p&gt;We closed that leak with LiveKit Cloud: a Next.js client for the call UI, our existing Laravel API for users and permissions, and LiveKit for the media layer. This post covers both the architectural decision and a working map of the integration—enough for another developer to follow the same shape in their own stack.&lt;/p&gt;

&lt;p&gt;"A third-party meeting link is a meeting in someone else’s product. Embedded video is a feature of yours."&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Post Covers
&lt;/h2&gt;

&lt;p&gt;Why product teams embed video calling instead of sending users to Zoom or Meet.&lt;/p&gt;

&lt;p&gt;How LiveKit compares with Daily, Agora, Twilio, Vonage, Amazon Chime, Zoom, and self-hosted SFUs.&lt;/p&gt;

&lt;p&gt;Cloud vs. self-hosted LiveKit and why we started on Cloud.&lt;/p&gt;

&lt;p&gt;The core code terms you will encounter: SFU, adaptive streaming, dynacast, rooms, and tokens.&lt;/p&gt;

&lt;p&gt;The architecture and actual code snippets used to join a call, handle audio vs. video, support guests, and manage recordings.&lt;/p&gt;

&lt;p&gt;How LiveKit bills participant-minutes (e.g., a 1-minute call with 5 people equals 5 WebRTC minutes).&lt;/p&gt;

&lt;p&gt;Why Embed Video Calling Instead of a Zoom Link?&lt;br&gt;
The fastest way to ship "video" is a third-party URL. Users already know Zoom, Meet, and Teams, and support teams know how to troubleshoot them. However, the hidden cost of that convenience shows up later:&lt;/p&gt;

&lt;p&gt;You lose the session: Notes, files, billing, and the next user action now live in another app under another brand.&lt;/p&gt;

&lt;p&gt;You lose access control: Your API already knows who paid, who is the host, and who is a guest. A shared meeting link does not.&lt;/p&gt;

&lt;p&gt;You lose the recording: It sits in the vendor’s history instead of next to the customer’s record in your database.&lt;/p&gt;

&lt;p&gt;You lose conversion: Opening Zoom, signing in, and waiting for the host introduces friction and drop-offs.&lt;/p&gt;

&lt;p&gt;An embedded video and audio call keeps the user inside the product. They are already logged in, the UI matches the rest of the app, and when the call ends, they remain on your page.&lt;/p&gt;

&lt;p&gt;Building that from raw WebRTC requires managing media servers, TURN relays, reconnects, and recordings. We didn't want to become a telecom company. We wanted native calling in Next.js, with our existing backend maintaining strict control over who can join. That is the exact gap a platform like LiveKit fills—and it is the kind of robust feature our custom software development services are built for.&lt;/p&gt;

&lt;h2&gt;
  
  
  LiveKit vs Zoom, Twilio, Daily, Agora, and the Rest
&lt;/h2&gt;

&lt;p&gt;The 2026 shortlist for putting video calling into an app is straightforward. We scored platforms based on escape hatches, developer experience, cost shapes for 1:1 and small groups, and whether recordings could land in storage we already own.&lt;/p&gt;

&lt;p&gt;Daily.co: A pleasure to integrate, billing roughly $0.004 per participant-minute after a free bucket. If the goal is a call on screen by lunch, it wins. It is a weaker escape hatch if you later want the media server under your own roof.&lt;/p&gt;

&lt;p&gt;Agora: Features a serious global mesh, especially in APAC. Audio looks cheap, but HD and Full-HD rates climb quickly. The stack is proprietary—fine if you are buying coverage, less fine if you want an open protocol.&lt;/p&gt;

&lt;p&gt;Twilio Video &amp;amp; Vonage Video: The familiar CPaaS path. Video is a separate product and a separate bill. Vonage (the old TokBox lineage) is a closer enterprise drop-in at around $0.0041 per minute. Both are closed SFUs.&lt;/p&gt;

&lt;p&gt;Amazon Chime SDK: Attractive if everything else is already on AWS at roughly $0.0017 per attendee-minute with one invoice and a clearer HIPAA story, though developer experience lags specialists.&lt;/p&gt;

&lt;p&gt;Zoom Video SDK:&lt;br&gt;
 Embeds Zoom directly. Fine if you want Zoom's brand and large-session muscle, but you are building on their product rather than an open protocol.&lt;/p&gt;

&lt;p&gt;mediasoup, Janus, or Jitsi: Puts you directly in the media-server business. A valid choice if you want to run an SRE rotation for one-way audio at 2 a.m., but not what we needed this quarter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why LiveKit Won
&lt;/h2&gt;

&lt;p&gt;We picked LiveKit because its trade-offs matched a product team rather than a telecom team:&lt;/p&gt;

&lt;p&gt;Cloud and self-hosted speak the same protocol: We could ship on *.livekit.cloud without a vendor-only client API. If data residency or unit economics later demand it, the same Next.js code can point at a server we operate.&lt;/p&gt;

&lt;p&gt;The React kit is a starting point, not a prison: Stock components got a conference on screen quickly, allowing us to replace the chrome without rewriting WebRTC.&lt;/p&gt;

&lt;p&gt;Grants matched the product: An audio call is a publish-source list. Guests are a different identity, but hosts and guests join the same room cleanly.&lt;/p&gt;

&lt;p&gt;Recordings land in our bucket: Cloud egress writes a grid layout straight to the S3 bucket we already use.&lt;/p&gt;

&lt;p&gt;The cost shape fits 1:1 and small groups: Connection minutes range between $0.0004–$0.0005 plus bandwidth as long as HD send and recording are monitored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud LiveKit vs. Running Your Own Server
&lt;/h2&gt;

&lt;p&gt;LiveKit is an open-source realtime media server with client SDKs for web and mobile. You can run it in two ways:&lt;/p&gt;

&lt;p&gt;Self-hosted: You operate livekit-server on Kubernetes or VMs, plus TURN for firewalls. At very high volume, this beats any vendor invoice. If you already scale infrastructure this way, our DevOps services run Kubernetes workloads day-to-day.&lt;/p&gt;

&lt;h2&gt;
  
  
  LiveKit Cloud
&lt;/h2&gt;

&lt;p&gt;: A managed project WebSocket URL, API keys, a global edge network, and dashboards. You pay a monthly plan plus usage and skip managing the SFU yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  How LiveKit Works: SFU, Adaptive Streaming, and Dynacast
&lt;/h2&gt;

&lt;p&gt;What is an SFU? A Selective Forwarding Unit is a media server in the middle of a call. Instead of every participant uploading multiple streams in a group call, each person uploads once, and the server forwards streams to everyone else.&lt;/p&gt;

&lt;p&gt;What is adaptive streaming? The client matches video quality to the size of the video tile on screen. Small thumbnails do not request 720p, saving significant bandwidth.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is dynacast
&lt;/h2&gt;

&lt;p&gt;: The publisher and SFU stop encoding quality layers that no active subscriber is watching.&lt;/p&gt;

&lt;p&gt;Turn both options on in your room options; they are the difference between a clean demo and an unmanageable bandwidth bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;: Next.js, Your API, and LiveKit Cloud&lt;br&gt;
The product creates a session and a unique room name (e.g., call-{uuid}).&lt;/p&gt;

&lt;p&gt;When a participant is authorized, the API signs a JWT and returns { token, server_url, room_name, identity }.&lt;/p&gt;

&lt;p&gt;Next.js passes the token and URL into LiveKit’s LiveKitRoom component.&lt;/p&gt;

&lt;p&gt;WebRTC handles media over wss://...livekit.cloud. Audio and video never transit your application servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Code Snippets
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. Install the SDKs
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"@livekit/components-react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.9.21"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"@livekit/components-styles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.2.0"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"livekit-client"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.19.2"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Keep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(config/livekit.php)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;PHP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;'url'&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;env('LIVEKIT_URL')&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;'api_key'&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;env('LIVEKIT_API_KEY')&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;'api_secret'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;env('LIVEKIT_API_SECRET')&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;'token_ttl'&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(int)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;env('LIVEKIT_TOKEN_TTL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Fetch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(app/services/calls.ts)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;TypeScript&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;LiveKitTokenResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;token:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;string;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;server_url:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;string;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;room_name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;string;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;identity:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;string;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;fetchCallToken(callId:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;string)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;apiClient.get&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;success:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;boolean;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;data:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;LiveKitTokenResponse;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;(`calls/$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;callId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;/token`);&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response.data.data;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Create Room and Mint Token (CallService.php)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="no"&gt;PHP&lt;/span&gt;
&lt;span class="nv"&gt;$roomName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'call-'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VideoCall&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'room_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'status'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;CallStatus&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Ringing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'mode'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// video | audio&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user-'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$actor&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$videoGrants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$call&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nc"&gt;CallMode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Audio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$videoGrants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'canPublishSources'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'microphone'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'screen_share'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'screen_share_audio'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;liveKitTokenService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;$call&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;room_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$actor&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'Participant'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$videoGrants&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="mf"&gt;5.&lt;/span&gt; &lt;span class="nc"&gt;Connect&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="nc"&gt;Next&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt; &lt;span class="nf"&gt;Room&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;VideoSession&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tsx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;TypeScript&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;roomKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;callId&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LiveKitRoom&lt;/span&gt;
  &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;roomKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;serverUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;serverUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;connect&lt;/span&gt;
  &lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;audio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;connectOptions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;autoSubscribe&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;adaptiveStream&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dynacast&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="n"&gt;onDisconnected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;onDisconnected&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;lk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PublishLocalTracks&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;callMode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CallConference&lt;/span&gt; &lt;span class="n"&gt;onEndCall&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;endCall&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;LiveKitRoom&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What LiveKit Cloud Costs
&lt;/h2&gt;

&lt;p&gt;LiveKit bills by participant-minutes (one person connected for one minute). For instance, a 1-minute call with 5 people accounts for 5 WebRTC minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build ($0/mo): Development tier, no card required.
&lt;/h2&gt;

&lt;p&gt;Ship (from $50/mo): Production use, email support, includes ~150,000 WebRTC minutes (then ~$0.0005/min).&lt;/p&gt;

&lt;p&gt;Scale (from $500/mo): Region pinning, HIPAA BAA, includes ~1.5 million minutes (then ~$0.0004/min).&lt;/p&gt;

&lt;h2&gt;
  
  
  Bandwidth:
&lt;/h2&gt;

&lt;p&gt;Billed separately around $0.10–$0.12 per GB after your included bucket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Embedded video calling is not a quick widget you drop in after lunch, but it shouldn't require running a dedicated media cluster yourself. LiveKit Cloud strikes the ideal middle ground: managed WebRTC infrastructure, secure JWT tokens minted via your API, and fully customizable React components.&lt;br&gt;
Originally posted by &lt;a href="https://omax.tech/blog/how-to-add-livekit-video-calling-to-a-next-js-app" rel="noopener noreferrer"&gt;Omax Tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>AI Writes the Code • Stop Blindly Shipping It Without Reviewing the Engineering</title>
      <dc:creator>omaxtech</dc:creator>
      <pubDate>Mon, 12 Oct 2026 14:02:41 +0000</pubDate>
      <link>https://dev.to/omaxtech2025/ai-writes-the-code-stop-blindly-shipping-it-without-reviewing-the-engineering-277b</link>
      <guid>https://dev.to/omaxtech2025/ai-writes-the-code-stop-blindly-shipping-it-without-reviewing-the-engineering-277b</guid>
      <description>&lt;p&gt;&lt;strong&gt;The Problem: AI Makes Coding Faster, Not Engineering Automatic&lt;br&gt;
AI has changed how quickly we can write software.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Give an AI coding tool a requirement, describe the expected behavior, and within seconds you can have a working implementation. Functions are generated, APIs are connected, database queries are written, tests are suggested, and sometimes an entire feature can be scaffolded before you have finished your coffee.&lt;/p&gt;

&lt;p&gt;That sounds like a huge win. And it is. But there is a problem.&lt;/p&gt;

&lt;p&gt;We are getting very good at generating code, while becoming dangerously comfortable with not reviewing the engineering behind that code.&lt;/p&gt;

&lt;p&gt;A feature can work perfectly in development and still be badly engineered. It can pass tests and still create unnecessary database queries. It can produce the correct response and still introduce duplicated business logic. It can solve today's requirement and make the next requirement significantly harder to implement.&lt;/p&gt;

&lt;p&gt;This is where the conversation around AI-generated code needs to change.&lt;/p&gt;

&lt;p&gt;The question shouldn't simply be:&lt;/p&gt;

&lt;p&gt;"Does the AI-generated code work?"&lt;/p&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;p&gt;"Is this the right engineering solution?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Is Extremely Good at Producing Code&lt;/strong&gt;&lt;br&gt;
Modern AI coding tools are surprisingly capable. They can understand existing files, follow patterns, generate functions, refactor components, write SQL, create API endpoints, and even make changes across multiple parts of an application.&lt;/p&gt;

&lt;p&gt;For developers, this can remove a lot of repetitive work. Instead of spending 30 minutes writing boilerplate, you can describe what you need and have a starting point almost immediately.&lt;/p&gt;

&lt;p&gt;That part is great. The danger begins when developers treat generated code as a finished engineering solution simply because it compiles and produces the expected result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compilation is not architecture.&lt;br&gt;
Passing tests is not scalability.&lt;br&gt;
Working code is not automatically good code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI is optimizing for the implementation it believes satisfies the request. It doesn't automatically understand every architectural decision your application has accumulated over years of development. Even when it understands the codebase, it may still choose a solution that is locally correct but globally expensive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "It Works" Trap&lt;/strong&gt;&lt;br&gt;
One of the easiest traps with AI-generated code is the phrase: “It works.”&lt;/p&gt;

&lt;p&gt;Imagine you ask AI to implement a feature that needs to process 1,000 employees. The generated implementation may:&lt;/p&gt;

&lt;p&gt;Fetch employees&lt;/p&gt;

&lt;p&gt;Loop through them&lt;/p&gt;

&lt;p&gt;Make another database query for each employee&lt;/p&gt;

&lt;p&gt;Perform additional API calls inside the loop&lt;/p&gt;

&lt;p&gt;Transform the data&lt;/p&gt;

&lt;p&gt;Save the results individually&lt;/p&gt;

&lt;p&gt;With a small development dataset, everything looks fine. The feature works. The tests pass. The ticket can be marked complete.&lt;/p&gt;

&lt;p&gt;Then production gets real data. Suddenly, a process that worked perfectly for 20 employees becomes painfully slow for 20,000.&lt;/p&gt;

&lt;p&gt;The problem wasn't necessarily that the AI generated invalid code. The problem was that nobody reviewed the engineering implications of the generated solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Engineering Questions Developers Must Ask&lt;/strong&gt;:&lt;br&gt;
Can this query be optimized?&lt;/p&gt;

&lt;p&gt;Am I creating an N+1 query problem?&lt;/p&gt;

&lt;p&gt;Can these records be processed in batches?&lt;/p&gt;

&lt;p&gt;Can I reduce the number of API calls?&lt;/p&gt;

&lt;p&gt;Should this operation be asynchronous?&lt;/p&gt;

&lt;p&gt;What happens when the dataset becomes 10× larger?&lt;/p&gt;

&lt;p&gt;What happens when multiple users trigger this simultaneously?&lt;/p&gt;

&lt;p&gt;These are engineering questions. And they cannot always be answered by simply checking whether the generated code produces the expected output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Can Write More Code Than You Actually Need&lt;/strong&gt;&lt;br&gt;
Another common problem is unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Ask AI to implement a relatively simple requirement, and you may receive a solution containing multiple helper functions, abstractions, configuration objects, defensive checks, wrappers, and layers of logic that weren’t actually necessary.&lt;/p&gt;

&lt;p&gt;Individually, every piece may look reasonable. Together, they can create a system that is harder to understand than the original problem.&lt;/p&gt;

&lt;p&gt;This is especially dangerous because generated code often looks professional: good naming, clean formatting, type definitions, comments, and reusable functions. Everything appears polished. But clean-looking code can still be over-engineered.&lt;/p&gt;

&lt;p&gt;Sometimes the best engineering solution is not another abstraction. Sometimes it is simply a small function that does one thing clearly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Real Test Is Often Production&lt;/strong&gt;&lt;br&gt;
Development environments are forgiving. Datasets are smaller. Traffic is lower. Fewer users are performing operations simultaneously.&lt;/p&gt;

&lt;p&gt;Production is where engineering decisions become visible. A solution that looks perfectly reasonable during development can behave very differently when:&lt;/p&gt;

&lt;p&gt;Thousands of records are processed simultaneously&lt;/p&gt;

&lt;p&gt;Database traffic spikes unpredictably&lt;/p&gt;

&lt;p&gt;External APIs become slow or rate-limited&lt;/p&gt;

&lt;p&gt;Memory usage grows unexpectedly&lt;/p&gt;

&lt;p&gt;Background jobs overlap and cause race conditions&lt;/p&gt;

&lt;p&gt;This is why AI-generated code deserves the same engineering review as code written by another developer. In fact, it deserves more scrutiny because it can be produced so quickly that the temptation to merge it without deeply understanding it is much higher.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Should Accelerate Engineering • Not Replace It&lt;/strong&gt;&lt;br&gt;
The goal shouldn't be to stop using AI. Quite the opposite. We should use it aggressively where it provides value.&lt;/p&gt;

&lt;p&gt;Let AI handle repetitive implementation, generate boilerplate, suggest approaches, write initial drafts, investigate bugs, generate tests, and explain unfamiliar code.&lt;/p&gt;

&lt;p&gt;But when the code is generated, the engineer still owns the decision:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is this implemented this way?&lt;br&gt;
Does this fit our architecture?&lt;br&gt;
Will this scale?&lt;br&gt;
Are we introducing duplication?&lt;br&gt;
Are we making the next change harder?&lt;br&gt;
What happens when this fails?&lt;br&gt;
What happens when the data grows?&lt;/strong&gt;&lt;br&gt;
AI doesn't eliminate the need for engineering judgment. It makes it more critical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Developer's Role Is Changing&lt;/strong&gt;&lt;br&gt;
AI doesn't make developers less important; it changes where their value is applied.&lt;/p&gt;

&lt;p&gt;When writing every line manually was the bottleneck, programming skill was heavily associated with syntax speed. With AI, producing those lines is becoming cheap.&lt;/p&gt;

&lt;p&gt;Higher-level skills are now paramount: understanding systems, making architectural decisions, recognizing trade-offs, reviewing code, thinking about failure modes, and knowing when the simplest solution is the better solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Writes the Code, Engineering Still Owns the Decision&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the code writes itself, engineering teams must focus on architectural ownership, performance tuning, and scalable design. If you're looking to scale your engineering team with senior architecture oversight, feel free to explore Omax Tech for specialized engineering and custom &lt;a href="https://omax.tech/blog/stop-blindly-shipping-ai-code" rel="noopener noreferrer"&gt;software development&lt;/a&gt; solutions.&lt;/p&gt;

&lt;p&gt;Originally posted by &lt;a href="https://omax.tech/" rel="noopener noreferrer"&gt;Omax Tech&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
