3

I'm trying to resolve a file path in NextJS.

I understand that API routes are working a little bit differently when deployed to Vercel. In order to create a correct path to the file I assumed I had to do this:

const svg = fs.readFileSync(
  path.join(process.cwd(), "img", "file.svg"),
  "utf-8",
);

// ENOENT: no such file or directory

But I cannot make it work. The file cannot be found under that path.

How can I find the correct path for a file in NextJS api routes?

I've followed the documentation of this.

  • Next version is: 11.1.3
  • When logging the path, it is giving /var/task/packages/project-root/img/file.svg
supersize
  • 13,764
  • 18
  • 74
  • 133
  • what error message are you getting back? – fredrivett Nov 23 '22 at 08:52
  • @fredrivett file not found under said path – supersize Nov 23 '22 at 09:10
  • try using `__dirname` to reach your desired path instead of `process.cwd()` – Moein Moeinnia Nov 24 '22 at 07:52
  • Thanks, but does not work. – supersize Nov 24 '22 at 08:46
  • @supersize Am I correct in assuming that your `pages` folder is on the same level as your `svg` folder? – Jonathan Wieben Nov 24 '22 at 14:29
  • @JonathanWieben I guess you are referring to the `img` folder, but yes you are correct. Both in root. – supersize Nov 24 '22 at 17:36
  • @supersize Yes, I did. However, the logged path in your message reads `/svg/file.svg` while in your code it says `img`. Are you positive you are referencing the correct path? I tried doing the same thing you are doing in one of my next apps and it did work. – Jonathan Wieben Nov 24 '22 at 18:15
  • @JonathanWieben yeah that is not the issue, it was a typo while formulating the question. I amended it to be correct. I double-checked this multiple times. – supersize Nov 24 '22 at 21:16
  • @supersize I simulated this scenario and this works both locally and on production (Vercel). Can you paste the error from Vercel function logs (https://vercel.com/docs/concepts/deployments/logs) here? – Son Nguyen Nov 29 '22 at 21:51
  • @SonNguyen sure, the error is `ENOENT: no such file or directory, open '/var/task/packages/frontend/img/file.svg'` – supersize Nov 30 '22 at 11:18
  • @supersize no luck, I cant reproduce your error. Desperate question, is there any chance your project is a monorepo, because I see the packages folder. – Son Nguyen Dec 01 '22 at 02:47
  • I don't think next.js API routes supports file reading without some workarounds. These links might help: https://github.com/vercel/next.js/discussions/32236 and https://medium.com/@boris.poehland.business/next-js-api-routes-how-to-read-files-from-directory-compatible-with-vercel-5fb5837694b9 – Nathan Dec 01 '22 at 05:19

5 Answers5

0

Try using

path.resolve("img", "file.svg")

Maybe it should help.

Sparko Sol
  • 651
  • 1
  • 9
0

Pretty sure you'll find the file if you serve it as a static file - Next.js documentation here

I'm thinking it's not bundled in the deployment, but whatever you have in /public will definitely be deployed.

Good luck

Oded Ben Dov
  • 9,936
  • 6
  • 38
  • 53
  • I wish it would be true, but it does not work with `public` either. – supersize Nov 29 '22 at 17:27
  • Your code does work for me locally. The problem is when deployed to Vercel? – Oded Ben Dov Nov 29 '22 at 19:44
  • This works for me on Vercel: fs.readdirSync('./public/img') – Oded Ben Dov Nov 29 '22 at 19:46
  • Correct, only failing when deployed to Vercel. Can you confirm your code example `fs.readdirSync('./public/img')`works for an api route? – supersize Nov 29 '22 at 20:49
  • You’re right, it doesn’t work from api. Doesn’t find the file. The same function works from getStaticProps (the website is built around it) but not /api/ ‍♂️ probably the dynamic vs static environments... If you put it in /public/ can't you just "import" the file and use it within the code? Next.js should take care of the magic wiring... Good luck! – Oded Ben Dov Nov 29 '22 at 21:45
0

I manage to create a small sandbox that will clarify your issue. Open it using StackBlitz

Project Structure

.
├── pages
|   ├── api
|   |   ├── hello.js
|   ├── _app.js 
|   ├── index.js
├── public
|   ├── 1.txt --> this is a demonstration file

I reproduce your code in the hello api for testing purposes

const { readFileSync } = require('fs');
const { join } = require('path');

export default (req, res) => {
  const path = join(process.cwd(), '/public/1.txt');
  const value = readFileSync(path, { encoding: 'utf-8' });
  res.status(200).json({ value });
};

This API entry is called from the index.js file

import Head from 'next/head';
import { useEffect, useState } from 'react';

export default function Home() {
  const [value, setValue ] = useState('');

  useEffect(() => {
    fetch('/api/hello')
    .then((res) => res.json())
    .then(data => setValue(data.value));
   });
   
  return (
    <div>
      <Head>
        <title>Create Next App</title>
      </Head>

      <main>
        <h1>{value}</h1>
      </main>
    </div>
  );
}

Yes, this is a very simplified version (for testing purposes only.. I assume we won't use readFileSync in production) - but - it reproduces your code.

Unfortunately, it works perfectly fine in dev mode and in production mode (npm run build + npm start), which means:

  • You either misconfigured your img folder
  • Perhaps you are lacking read permissions for the path you are using. For instance if you deploy your work to a remote machine, most directories will have limited access and therefore prevent you from reading the file (for testing this theory please read this post and execute it on your deployed machine)
ymz
  • 6,602
  • 1
  • 20
  • 39
  • it works fine for me locally as well. It stops working for Vercel deployments. I will recreate your code once more exactly like that. – supersize Nov 30 '22 at 09:52
  • Yeah it does not work. I am getting the same error in the public folder with the exact same setup as yours. – supersize Nov 30 '22 at 10:35
  • Also this example does not deploy to Vercel, which is the problem I think. – supersize Nov 30 '22 at 12:24
  • that's why I added the `permissions` section to my answer. please verify that you have proper access rights to the path that you are trying to read files from (and please use the link I 've added to properly execute it) – ymz Nov 30 '22 at 14:30
  • first of all I appreciate your input on this. I don't think this will be the issue as the folders are created by running the next command and they didn't get changed. But nevertheless I created a new app and by comparing it I can see that both folders are the same chmod rights: `drwxr-xr-x`, so unfortunately this isn't it either. – supersize Nov 30 '22 at 17:24
  • ok.. can you please share which value your `outputDirectory` field has inside `vercel.json` config file? – ymz Nov 30 '22 at 18:16
  • btw.. have you downloaded the sandbox l that I created locally to your computer ant try it? just ensure that you know that this project can be downloaded as zip and properly edited locally – ymz Nov 30 '22 at 18:18
  • should the `vercel.json` be in the root of Next? Because we don't have one then. – supersize Nov 30 '22 at 18:28
  • according to the docs https://vercel.com/docs/concepts/deployments/configure-a-build#output-directory - if you omit configuration the `public` folder is set to default. hence, using the `pulic` folder should work. if you are still suing your `img` folder instead than yes - you should add `vercel.json` file and configure it – ymz Nov 30 '22 at 21:09
0

For anyone coming across this, I actually opened a ticket at Vercel to ask them about this.

It turns out it was a caching issue that is caused by using Yarn 3

The support redirected me to this page explaining that they would have issues with anything above Yarn 1.

According to them there is nothing really they can do about right now but suggest us to use a different package manager.

supersize
  • 13,764
  • 18
  • 74
  • 133
0

I'm using Yarn 1.22, but still have this issue. The reason is because files are not generated during build and run times, so they are never found. The way to get around this is to create a separate .js file that to wrap around the said static files (html, txt, etc). Export this JS object which contains the files, and Vercel will generate them. I'm using this to generate email templates.

//account_verify.js
import path from 'path';
import { promises as fs } from 'fs';
import { prefixPath } from './constants';

// TODO: force this to conform to a typescript type
export default {
    subject: 'Confirm Your Account',
    data: {
      email_verification_link: '{{email_verification_link}}',
      first_name: '{{first_name}}'
    },
    templates: {
      txt: fs.readFile(path.join(process.cwd(), prefixPath, 'account_verify.txt'), 'utf8'),
      html: fs.readFile(path.join(process.cwd(), prefixPath, 'account_verify.html'), 'utf8'),
    }
  };
guest
  • 2,185
  • 3
  • 23
  • 46