Powered by AppSignal & Oban Pro

Mock API for Backend Interview

playground/mock-server.livemd

Mock API for Backend Interview

Mix.install([
  {:kino, "~> 0.18.0"},
  {:plug, "~> 1.19"}
])

Mock Server

defmodule Fixtures do
  def user_feed("crocs", _cursor = nil) do
    json = %{
      "items" => [
        %{
          "pk" => 3_783_929_427_378_941_615,
          "caption" => "you: MiSsEd OpPoRtUniTy FoR hAnDsOmE sQuIdWaRd\nus:",
          "shortcode" => "DSDN8QeEa6v",
          "media_type" => "reel",
          "video_url" =>
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/9/b4xINRf5Ni2bCXwUkQCpjFljimziqRh85/IG_reel_crocs_2025-12-09.mp4",
          "poster_url" =>
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/9/b4xINRf5Ni2bCXwUkQCpjFljimziqRh85/IG_reel_crocs_2025-12-09_poster.jpg",
          "mentions" => ["nickelodeon"],
          "taken_at" => 1_765_299_644,
          "author" => %{
            "full_name" => "Crocs Shoes",
            "id" => "199722566",
            "username" => "crocs"
          }
        },
        %{
          "pk" => 3_783_205_366_231_282_217,
          "caption" => "maturing is realizing you're more Squidward than SpongeBob.",
          "shortcode" => "DSApTx-kTIp",
          "media_type" => "carousel",
          "carousel_urls" => [
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/8/GzEhRLUGqclwT29IKNhvvF1Xcg8UmXFqp/IG_carousel_0_crocs_2025-12-08.jpg",
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/8/GzEhRLUGqclwT29IKNhvvF1Xcg8UmXFqp/IG_carousel_1_crocs_2025-12-08.jpg",
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/8/GzEhRLUGqclwT29IKNhvvF1Xcg8UmXFqp/IG_carousel_2_crocs_2025-12-08.jpg",
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/8/GzEhRLUGqclwT29IKNhvvF1Xcg8UmXFqp/IG_carousel_3_crocs_2025-12-08.jpg",
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/8/GzEhRLUGqclwT29IKNhvvF1Xcg8UmXFqp/IG_carousel_4_crocs_2025-12-08.jpg"
          ],
          "mentions" => ["nickelodeon", "spongebob"],
          "taken_at" => 1_765_213_245,
          "author" => %{
            "full_name" => "Crocs Shoes",
            "id" => "199722566",
            "username" => "crocs"
          }
        }
      ],
      "next_cursor" => "2"
    }

    {:ok, json}
  end

  def user_feed("crocs", _cursor = "2") do
    json = %{
      "items" => [
        %{
          "pk" => 3_780_294_121_744_335_139,
          "caption" =>
            "\"this is the worst day of my life.\" - Squidward (and us) #CrocsPartner #crocssale",
          "shortcode" => "DR2TXmREYEj",
          "media_type" => "post",
          "image_url" =>
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/4/aMGh2ZF8buKjsEoTQKcVKhOvidgSwji9r/IG_post_crocs_2025-12-04.jpg",
          "mentions" => ["nickelodeon", "spongebob"],
          "taken_at" => 1_764_866_198,
          "author" => %{
            "full_name" => "Crocs Shoes",
            "id" => "199722566",
            "username" => "crocs"
          }
        }
      ]
    }

    {:ok, json}
  end

  def user_feed("crocs", _cursor) do
    {:ok, %{}}
  end
  
  def user_feed("neymarjr", _cursor = nil) do
    json = %{
      "items" => [
        %{
          "pk" => 3_767_537_624_220_035_822,
          "caption" =>
            "Ele é O nome da resenha, não tem jeito! Nosso embaixador Neymar Jr. tá on com seus parças e chega com uma linha de snacks Canção para deixar a sua
  resenha ainda mais gostosa. Vai ser difícil sobrar um para o after. 😉🧡\n\nConta pra gente usando a hashtag #MinhaResenhaCanção: Qual é a sua resenha
  preferida?\n\n#CançãoAlimentos  #CadaMomentoUmaHistória",
          "video_url" =>
            "https://ik.imagekit.io/archivecdn/facebook/2025/11/17/OQahLnSGwUzzF8atd2FyXf9mU2OTkBIlB/IG_reel_cancaoalimentos_2025-11-17.mp4",
          "poster_url" =>
            "https://ik.imagekit.io/archivecdn/facebook/2025/11/17/OQahLnSGwUzzF8atd2FyXf9mU2OTkBIlB/IG_reel_cancaoalimentos_2025-11-17_poster.jpg",
          "shortcode" => "DRI-4IJjeru",
          "media_type" => "reel",
          "mentions" => ["neymarjr", "rafatortola", "neymarjrsiteoficial", "nrsports"],
          "taken_at" => 1_763_345_515,
          "author" => %{
            "full_name" => "Canção Alimentos",
            "id" => "3592384122",
            "username" => "cancaoalimentos"
          }
        },
        %{
          "pk" => 3_783_485_208_927_313_668,
          "caption" => "🤍🖤🙏",
          "shortcode" => "DSBo8BzDp8E",
          "media_type" => "post",
          "image_url" =>
            "https://ik.imagekit.io/archivecdn/facebook/2025/12/9/gG9hoMil7tvoiW8Ujgtpef1bh2XhdeUPY/IG_post_neymarjr_2025-12-09.jpg",
          "mentions" => [],
          "taken_at" => 1_765_246_605,
          "author" => %{
            "full_name" => "Neymar Jr",
            "id" => "26669533",
            "username" => "neymarjr"
          }
        }
      ]
    }

    {:ok, json}
  end
  
  def user_feed("neymarjr", _cursor) do
    {:ok, %{}}
  end

  def user_feed(_username, _cursor) do
    {:error, :not_found}
  end

  def transcription do
    %{
      "duration" => 2.52,
      "language" => "en",
      "segments" => [
        %{
          "avg_logprob" => -0.23063973,
          "compression_ratio" => 1.7335092,
          "end" => 1.44,
          "id" => 0,
          "no_speech_prob" => 1.6620296e-12,
          "seek" => 0,
          "start" => 0,
          "temperature" => 0,
          "text" => " I got a big surprise for you.",
          "tokens" => [50365, 286, 658, 257, 955, 6365, 337, 291, 13, 50437]
        },
        %{
          "avg_logprob" => -0.25,
          "compression_ratio" => 1.6,
          "end" => 2.52,
          "id" => 1,
          "no_speech_prob" => 1.5e-12,
          "seek" => 0,
          "start" => 1.44,
          "temperature" => 0,
          "text" => " You're actually gonna love this.",
          "tokens" => [50437, 509, 434, 767, 799, 959, 341, 13, 50491]
        }
      ],
      "task" => "transcribe",
      "text" => " I got a big surprise for you. You're actually gonna love this.",
      "words" => [
        %{"end" => 0.06, "start" => 0, "word" => "I"},
        %{"end" => 0.16, "start" => 0.06, "word" => "got"},
        %{"end" => 0.26, "start" => 0.16, "word" => "a"},
        %{"end" => 0.38, "start" => 0.26, "word" => "big"},
        %{"end" => 0.84, "start" => 0.38, "word" => "surprise"},
        %{"end" => 1.12, "start" => 0.84, "word" => "for"},
        %{"end" => 1.44, "start" => 1.12, "word" => "you."},
        %{"end" => 1.64, "start" => 1.44, "word" => "You're"},
        %{"end" => 1.88, "start" => 1.64, "word" => "actually"},
        %{"end" => 2.04, "start" => 1.88, "word" => "gonna"},
        %{"end" => 2.24, "start" => 2.04, "word" => "love"},
        %{"end" => 2.52, "start" => 2.24, "word" => "this."}
      ]
    }
  end
end
defmodule ApiRouter do
  use Plug.Router

  plug(:match)
  plug(Plug.Parsers, parsers: [:json], json_decoder: JSON)
  plug(:dispatch)

  get "/user_feed/:username" do
    username = conn.path_params["username"]
    cursor = conn.query_params["cursor"]

    case Fixtures.user_feed(username, cursor) do
      {:ok, json} ->
        conn
        |> put_resp_content_type("application/json")
        |> send_resp(200, JSON.encode!(json))

      {:error, :not_found} ->
        conn
        |> put_resp_content_type("application/json")
        |> send_resp(404, ~s({"message": "user not found"}))
    end
  end

  post "/publish" do
    conn.body_params

    dbg(conn.body_params)

    conn
    |> put_resp_content_type("application/json")
    |> send_resp(200, JSON.encode!(conn.body_params))
  end

  post "/transcription" do
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(200, JSON.encode!(Fixtures.transcription()))
  end

  match _ do
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(404, ~s({"message": "route not found"}))
  end
end

Kino.Proxy.listen(ApiRouter)