Benjamin Higginbotham
Benjamin Higginbotham

Reputation: 261

.map is not a function Vercel Deploy

Normally if I get an error when using .map it's because the array on which I'm using the method either doesn't exist or isn't an array at all.

Sometimes I return a JSON object from a fetch and I try to use .map on the object instead of an array that's inside of the object.

These are rookie errors, and I am a rookie. However, in the following case I'm sure that I am calling the .map on an array that is inside of a JSON object.

How?

  1. Because the following code works locally in development and
  2. Because I console.log(albums) to make damn sure that I was iterating over an array.

When I deploy this next.js app on Vercel, I get the following error:

> Build error occurred
21:23:30    TypeError: albums.map is not a function
21:23:30        at getStaticPaths (/vercel/workpath0/.next/serverless/pages/albums/[id].js:306:16)
21:23:30        at processTicksAndRejections (internal/process/task_queues.js:97:5)
21:23:30        at async buildStaticPaths (/vercel/workpath0/node_modules/next/dist/build/utils.js:16:80)
21:23:30        at async Object.isPageStatic (/vercel/workpath0/node_modules/next/dist/build/utils.js:23:549) {
21:23:30      type: 'TypeError'
21:23:30    }
21:23:30    error Command failed with exit code 1.
21:23:30    info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
21:23:30    Error: Command "yarn run build" exited with 1
21:23:32    Done with "package.json"

Here's the page this error is referring to:

import React from "react";
import { useQuery, useMutation, useQueryClient } from "react-query";
import Image from "next/image";
import { queryAlbum } from "../../api/albums/[id]";
import { queryAlbums } from "../../api/albums";
import Form from "../../../components/styles/Form";
import Button from "../../../components/styles/Button";
import Container from "../../../components/styles/AlbumsShow";

async function createPicture(pictureData) {
  const response = await fetch(`/api/pictures/create`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(pictureData),
  });
  const { picture } = response.json();
  return picture;
}

async function deletePicture(pictureId) {
  await fetch(`/api/pictures/delete`, {
    method: "DELETE",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(pictureId),
  });
}

export async function getStaticPaths() {
  const albums = queryAlbums();
  const paths = [];
  await albums.map((album) => {
    return paths.push({
      params: {
        id: album.id.toString(),
      },
    });
  });
  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({ params }) {
  const response = await queryAlbum(params.id);
  const data = await JSON.stringify(response);
  const initialAlbumData = await JSON.parse(data);
  return {
    props: {
      initialAlbumData,
    },
  };
}

export default function Album({ initialAlbumData }) {
 ...all the code for the component 
  );
}

and here are the query functions:

import { PrismaClient } from "@prisma/client";

export async function queryAlbum(albumId) {
  const prisma = new PrismaClient({ log: ["query"] });

  try {
    const album = await prisma.album.findUnique({
      where: {
        id: parseInt(albumId),
      },
      include: {
        pictures: true,
      },
    });
    return album;
  } catch (error) {
    return { error: error };
  } finally {
    prisma.$disconnect();
  }
}

export default async function answerQuery(req, res) {
  // get [id] from url
  const {
    query: { id },
  } = req;
  const album = await queryAlbum(id);
  res.json({ album: album });
}

and:

import { PrismaClient } from "@prisma/client";

export async function queryAlbums() {
  const prisma = new PrismaClient({ log: ["query"] });

  try {
    const albums = await prisma.album.findMany();
    return albums;
  } catch (error) {
    return { error: error };
  } finally {
    prisma.$disconnect();
  }
}

export default async function Albums(req, res) {
  const albums = await queryAlbums();
  res.json({ albums: albums });
}

For a little context on why I chose to import queryAlbum and queryAlbums, here's a thread on Git that I used to base my code on: https://github.com/vercel/next.js/discussions/13648

Upvotes: 2

Views: 2949

Answers (2)

Benjamin Higginbotham
Benjamin Higginbotham

Reputation: 261

I solved this problem in two steps.

First, I found an answer on Git: https://github.com/vercel/next.js/issues/8041. Basically, all I needed to do was delete the "babel" configurations. I really don't know why this answer works. I'll look into it and post updates here.

package.json that causes the error on deploy:

{
  "name": "photo_album",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "prisma": "prisma",
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "@prisma/client": "^2.13.1",
    "babel-plugin-styled-components": "^1.12.0",
    "next": "10.0.3",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-query": "^3.5.1",
    "styled-components": "^5.2.1"
  },
  "devDependencies": {
    "@prisma/cli": "^2.13.1"
  },
  "babel": {
    "presets": [
      "@babel/preset-react"
    ],
    "env": {
      "development": {
        "presets": [
          "next/babel",
          "@babel/preset-react"
          "next/babel"
        ],
        "plugins": [
          [

the answer:

{
  "name": "photo_album",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "prisma": "prisma",
    "dev": "next dev",
    "build": "next build",
    "start": "next start -p $PORT",
    "postinstall": "prisma generate",
    "heroku-postbuild": "npm run build"
  },
  "dependencies": {
    "@prisma/client": "^2.13.1",
    "babel-plugin-styled-components": "^1.12.0",
    "next": "10.0.3",
    "react": "16.13.0",
    "react-dom": "17.0.1",
    "react-query": "^3.5.1",
    "styled-components": "^5.2.1",
    "@prisma/cli": "^2.13.1",
    "@babel/core": "^7.12.10",
    "@babel/preset-react": "^7.12.10"
  },
  "devDependencies": {
    "@babel/core": "^7.12.10",
    "@babel/preset-react": "^7.12.10",
    "@prisma/cli": "^2.13.1"
  }
}

The second step involved changing getStaticProps to getServerSideProps and DELETING getStaticPaths. Here's what getServerSideProps looks like:

export async function getServerSideProps(context) {
  const response = await queryAlbum(context.query.id);
  const data = await JSON.stringify(response);
  const initialAlbumData = await JSON.parse(data);

  if (!initialAlbumData) {
    return {
      notFound: true,
    };
  }

  return {
    props: {
      initialAlbumData,
    },
  };
}

Upvotes: 1

Nilesh Patel
Nilesh Patel

Reputation: 3317

Looking at the import, seems these are api you created. These are not normal methods, they are http methods, can be consume over http(http call).

import { queryAlbum } from "../../api/albums/[id]";
import { queryAlbums } from "../../api/albums";

I am using fetch to consume respective apis.

export async function getStaticPaths() {
  const data = await fetch('/api/albums');
  const albums = await data.json();
  const paths = await albums.map((album) => {
    return paths.push({
      params: {
        id: album.id.toString(),
      },
    });
  });
  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({ params }) {
  const response = await fetch(`/api/albums/${params.id}`);
  const data = await response.json();
  return {
    props: {
      initialAlbumData: data,
    },
  };
}

Upvotes: 0

Related Questions