Error Creating View from API route

ChrisBB
ChrisBB Member
edited March 2023 in Q&A

Hello fellow dailyCo workers,

I am working on a NextJs app, and following the guide here: Use Next.js API routes to create Daily video chat rooms. However, a call to the API returned this error. I have done a lot of searching online to find an answer to no avail. Here is the error and related file contents:

error - SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    at JSON.parse (<anonymous>)
    at packageData (node:internal/deps/undici/undici:6456:23)
    at specConsumeBody (node:internal/deps/undici/undici:6434:14)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async handler (webpack-internal:///(api)/./pages/api/room/index.js:26:26)
    at async Object.apiResolver (path-to-local-dir)
    at async DevServer.runApi (path-to-local-dir)
    at async Object.fn (path-to-local-dir)
    at async Router.execute (path-to-local-dir)
    at async DevServer.run (path-to-local-dir) {
  page: '/api/room'


Here is what my api file looks like:

export default async function handler(req, res) {
  if (req.method === "POST") {
    const options = {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.DAILY_API_KEY}`,
      },
      body: JSON.stringify({
        properties: {
          enable_prejoin_ui: true,
          enable_network_ui: true,
          enable_screenshare: true,
          enable_chat: true,
          exp: Math.round(Date.now() / 1000) + 300,
          eject_at_room_exp: true,
        },
      }),
    };


    const dailyRes = await fetch(`${process.env.DAILY_DOMAIN}/rooms`, options);


    const response = await dailyRes.json();


    if (response.error) {
      return res.status(500).json(response.error);
    }


    return res.status(200).json(response);
  }


  return res.status(500);
}

Please note that the API_KEY and DOMAIN from my env file is exactly as it is shown here. Can someone help? Thanks in advance

Tagged:

Best Answer

  • daily_joey
    daily_joey Moderator, Dailynista admin
    Answer ✓

    Hey @ChrisBB ,

    Can you confirm the value of the variable DAILY_DOMAIN ?

    In the example from the page you linked, there's a minor difference around the endpoint URL:

    const dailyRes = await fetch(
         `${process.env.DAILY_REST_DOMAIN}/rooms`,
         options
       );
    

    In this case, the URL for DAILY_REST_DOMAIN would be https://api.daily.co/v1/ and not the Daily subdomain chosen during account creation.

    API requests are made to https://api.daily.co/v1, while room URLs contain your subdomain.

Answers

  • @daily_joey - Thanks for the prompt response, and suggestion. I was using mycompanyname.daily.co rather thank the one above. The fix is in - thanks. However, I have another issue - there are two instances of the video player. I was expecting just one (please see the attached image)

    How can I resolve this?

  • daily_joey
    daily_joey Moderator, Dailynista admin

    @ChrisBB it's hard to say without seeing the code for your client implementation but it seems like you might be instantiating the Daily Prebuilt iFrame multiple times unintentionally.

    A typical call flow might create the frame and join(), then at the end of a session leave() then destroy() to clean up the iFrame created by Daily. If you can share a code snippet that includes the createFrame method and how it is called, we can take a look.

  • Hello @daily_joey , Thanks again for the prompt response. Below is a copy of the code :

    import Call from "./Call";
    import Home from "./Home";
    import { useState } from "react";
    
    
    export default function ActivePool({ id, isConfigured = false }) {
      const [room, setRoom] = useState(null);
      const [expiry, setExpiry] = useState(null);
      const [callFrame, setCallFrame] = useState(null);
    
    
      return (
        <>
          <div className="min-h-full">
            <main className="py-10">
              <div className="mx-auto mt-8 grid max-w-3xl grid-cols-1 gap-6 sm:px-6 lg:max-w-7xl lg:grid-flow-col-dense lg:grid-cols-1">
                <div className="space-y-6 lg:col-span-2 lg:col-start-1">
                  {/* Video section*/}
                  <section aria-labelledby="applicant-information-title">
                    <div className="bg-white shadow sm:rounded-lg">
                      <div className="border-t border-gray-200 px-4 py-5 sm:px-6">
                        <dl className="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
                          <div className="sm:col-span-2">
                            <dt className="text-sm font-medium text-gray-500">
                              About
                            </dt>
                            <dd className="mt-1 text-sm text-gray-900">
                              {/* Video Lives here */}
                              {room ? (
                                <Call
                                  room={room}
                                  expiry={expiry}
                                  setRoom={setRoom}
                                  setCallFrame={setCallFrame}
                                  callFrame={callFrame}
                                />
                              ) : (
                                <Home
                                  setRoom={setRoom}
                                  setExpiry={setExpiry}
                                  isConfigured={isConfigured}
                                />
                              )}
                            </dd>
                          </div>
                        </dl>
                      </div>
                    </div>
                  </section>
                </div>
              </div>
            </main>
          </div>
        </>
      );
    }
    

    I'm thinking that it's rendering cos of the Call/Home, but that came from the Next.js snippet from one of your tutorials. Please advice how to restructure this

  • Hello @daily_joey , My last question was flagged for some reason, but Tasha from your team unflagged it. Should I expect a response today. Thanks. Also, please keep in mind that I am using NextJS framework. Can you explain how to use the .join as I cant find any tutorial on using daily.co with the framework. Thanks in advance.

  • ChrisBB
    ChrisBB Member
    edited March 2023

    Your tutorial on this page (https://www.daily.co/blog/use-next-api-routes-to-create-daily-rooms-dynamically/) is poorly written. I am getting an error `ReferenceError: useCallback is not defined` via `components/Call.js` following the steps from the docs. A few other lines of codes could have been written better, more clearly as well. Sorry for venting my frustrations on the thread.

  • Along with the above code snippet (march 19th), here is the code for the Call component:

    import { useCallback, useEffect, useRef, useState } from "react";
    // import Button from "@custom/shared/components/Button";
    // import {
    //   Card,
    //   CardBody,
    //   CardHeader,
    //   CardFooter,
    // } from "@custom/shared/components/Card";
    // import { TextInput } from "@custom/shared/components/Input";
    import DailyIframe from "@daily-co/daily-js";
    import { writeText } from "clipboard-polyfill";
    import ExpiryTimer from "./ExpiryTimer";
    
    
    const CALL_OPTIONS = {
      showLeaveButton: true,
      iframeStyle: {
        height: "100%",
        width: "100%",
        aspectRatio: 16 / 9,
        minwidth: "400px",
        maxWidth: "920px",
        border: "0",
        borderRadius: "12px",
      },
    };
    
    
    export function Call({ room, setRoom, callFrame, setCallFrame, expiry }) {
      const callRef = useRef(null);
      const [isLinkCopied, setIsLinkCopied] = useState(false);
    
    
      const handleCopyClick = useCallback(() => {
        writeText(room);
        setIsLinkCopied(true);
        setTimeout(() => setIsLinkCopied(false), 5000);
      }, [room, isLinkCopied]);
    
    
      const createAndJoinCall = useCallback(() => {
        const newCallFrame = DailyIframe.createFrame(
          callRef?.current,
          CALL_OPTIONS
        );
    
    
        setCallFrame(newCallFrame);
    
    
        newCallFrame.join({ url: room });
    
    
        const leaveCall = () => {
          setRoom(null);
          setCallFrame(null);
          callFrame.destroy();
        };
    
    
        newCallFrame.on("left-meeting", leaveCall);
      }, [room, setCallFrame]);
    
    
      /**
       * Initiate Daily iframe creation on component render if it doesn't already exist
       */
      useEffect(() => {
        if (callFrame) return;
    
    
        createAndJoinCall();
      }, [callFrame, createAndJoinCall]);
    
    
      return (
        <div>
          <div className="call-container">
            {/* Daily iframe container */}
            <div ref={callRef} className="call" />
            <div>
              <div>Copy and share the URL to invite others</div>
              <div>
                <label htmlFor="copy-url"></label>
                <input
                  type="text"
                  id="copy-url"
                  placeholder="Copy this room URL"
                  value={room}
                  pattern="^(https:\/\/)?[\w.-]+(\.(daily\.(co)))+[\/\/]+[\w.-]+$"
                />
                <button onClick={handleCopyClick}>
                  {isLinkCopied ? "Copied!" : `Copy room URL`}
                </button>
              </div>
              <div>
                {expiry && (
                  <div>
                    Room expires in:
                    <ExpiryTimer expiry={expiry} />
                  </div>
                )}
              </div>
            </div>
            <style jsx>{`
              .call-container {
                display: flex;
                align-items: center;
                gap: var(--spacing-md);
              }
              .call-container :global(.call) {
                width: 100%;
              }
              .call-container :global(.button) {
                margin-top: var(--spacing-md);
              }
              .call-container :global(.card) {
                max-width: 300px;
                max-height: 400px;
              }
              .call-container :global(.card-footer) {
                align-items: center;
                gap: var(--spacing-xxs);
              }
              .call-container :global(.countdown) {
                position: static;
                border-radius: var(--radius-sm);
              }
              @media only screen and (max-width: 750px) {
                .call-container {
                  flex-direction: column;
                }
              }
            `}</style>
          </div>
        </div>
      );
    }
    
    
    export default Call;
    
    
    

    The issue again is that the call video is displaying twice on the page

  • daily_joey
    daily_joey Moderator, Dailynista admin

    Hi @ChrisBB , thanks for the additional code - it looks like everything should be working, so I've asked my teammates to take a look. We'll also work on reviewing the demo you've been referencing, as it's one of our older ones.

  • Hello Joey,

    Please circle back with response from your team. Thank you for your support