Search code examples
google-app-enginenext.js13

Datastore connection in NEXT.JS (v13.5.6) project router.js file deploying to App Engine does not work, but works well locally


I have a NEXT.JS project (v13.5.6, using App Router), running on Google App Engine (Standard), I'm using Google Firestore in Datastore mode for storing some data there and all of these works well when I build and run the project locally on my laptop (windows), but when I try to deploy to Google App Engine it throws me this error:

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 62 files to Google Cloud Storage               ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...failed.
ERROR: (gcloud.app.deploy) Error Response: [9] Cloud build [hash] status: FAILURE
> [email protected] build
> next build

Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:   
https://nextjs.org/telemetry

   Creating an optimized production build ...
 ✓ Compiled successfully
   Linting and checking validity of types ...
   Collecting page data ...
   Generating static pages (0/19) ...
   Generating static pages (4/19)
   Generating static pages (9/19)
   Generating static pages (14/19)

Error occurred prerendering page "/robots.txt". Read more: https://nextjs.org/docs/messages/prerender-error
Error: 7 PERMISSION_DENIED: Missing or insufficient permissions.
    at callErrorFromStatus (/workspace/node_modules/@grpc/grpc-js/build/src/call.js:31:19)
    at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client.js:192:76)
    at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141)
    at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181)
    at /workspace/node_modules/@grpc/grpc-js/build/src/resolving-call.js:99:78
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
for call at
    at ServiceClientImpl.makeUnaryRequest (/workspace/node_modules/@grpc/grpc-js/build/src/client.js:160:32)
    at ServiceClientImpl.<anonymous> (/workspace/node_modules/@grpc/grpc-js/build/src/make-client.js:105:19)
    at /workspace/node_modules/@google-cloud/datastore/build/src/v1/datastore_client.js:224:29
    at /workspace/node_modules/google-gax/build/src/normalCalls/timeout.js:44:16
    at repeat (/workspace/node_modules/google-gax/build/src/normalCalls/retries.js:80:25)
    at /workspace/node_modules/google-gax/build/src/normalCalls/retries.js:118:13
    at OngoingCall.call (/workspace/node_modules/google-gax/build/src/call.js:67:27)
    at NormalApiCaller.call (/workspace/node_modules/google-gax/build/src/normalCalls/normalApiCaller.js:34:19)
    at /workspace/node_modules/google-gax/build/src/createApiCall.js:84:30
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
 ✓ Generating static pages (19/19)

> Export encountered errors on following paths:
        /robots.txt/route: /robots.txt
Full build logs: https://console.cloud.google.com/cloud-build/builds;region=[region]/[hash]?project=[project-id]

As far as I understand, Datastore should be accessible from the App Engine, since they are in the same Google project and I don't need to create a separate service/credentials for it, and it is working well for all pages in the app/ folder, but not for the route.ts files in api/ or robots.ts, sitemap.ts, manifest.ts.

Here is my package.json

{
  "name": "myproject",
  "version": "3.1.0",
  "description": "...description...",
  "private": true,
  "author": "Ivan Melnyk",
  "main": "next start",
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "deploy": "gcloud app deploy"
  },
  "dependencies": {
    "@google-cloud/datastore": "^8.2.1",
    "cookies-next": "^4.0.0",
    "date-fns": "^2.30.0",
    "next": "^13.5.6",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-icons": "^4.11.0",
    "server-only": "^0.0.1",
    "sharp": "^0.32.6"
  },
  "devDependencies": {
    "@types/node": "20.6.5",
    "@types/react": "18.2.22",
    "autoprefixer": "^10.4.16",
    "cssnano": "^6.0.1",
    "eslint-config-next": "^13.5.3",
    "eslint-config-prettier": "^9.0.0",
    "postcss": "^8.4.30",
    "sass": "^1.68.0",
    "tailwindcss": "^3.3.3",
    "typescript": "5.2.2"
  }
}

Here is the robots.ts

import { General } from 'data'

export default async function robots() {
    const data = await General()
    return {
        rules: {
            userAgent: '*',
            // disallow: '/'
            allow: '/',
            disallow: '/email',
        },
        sitemap: `${data.url}/sitemap.xml`,
    }
}

Here is the code of function General in data.ts

import 'server-only'
import { Datastore } from '@google-cloud/datastore'
export async function General() {
    const datastore = new Datastore()
    const [[records]] = await datastore.runQuery(datastore.createQuery('general'))
    return records
}

The table 'general' in Datastore has only one row of fields (like a config).

The same issue is reproducible for any route.ts (i.e. api/*/route.ts, ./robots.ts, ./sitemap.ts, ./manifest.ts).

I've tried also to create a route in api/ folder and then rewrite in next.config.js - did not help.

What I'm doing wrong?

The same code is working well locally on my laptop (windows). I'm making the connection to the same Datastore db on Google Cloud (production), not the emulator.


Solution

  • Found the solution and it is not related to Google Cloud. The latest Next.js release (13.5.6 at the moment) has an issue with the back slashes on build in Windows and upload to Linux (like Google AppEngine VM). I did not have the solution for this issue in the past so I've decided to run the build on AppEngine. As of today there is a temp fix for this issue, described here, I've applied this fix and now I don't need to build my packages remotely on AppEngine, I build locally and upload the build files to AppEngine, and it is working well, no issues with the back slashes, and no issues with the Datastore permission. Hope it will help anyone else.