Hosting a file along with my function

Hey!

I’d like to create a function which loads a PDF and performs some manipulations on it before returning it. I’ve put my PDF file in my functions directory but it seems that the built function is moved elsewhere before it’s served and the PDF is not taken with it. I can’t find any examples of doing a similar thing so is this just not possible?

With the following in my function:

const fs = require('fs')
fs.readFileSync('dummy.pdf')

I get the following error:

{"errorMessage":"ENOENT: no such file or directory, open 'dummy.pdf'","errorType":"Error","stackTrace":["Object.fs.openSync (fs.js:646:18)","Object.fs.readFileSync (fs.js:551:33)","e.handler (/var/task/hello.js:1:146237)"]}

Thanks ahead!

2 Likes

Problem solved. When using fs file paths are relative to the project root.

Actually, that’s only when serving locally - the file still cannot be read once deployed.

@voteymcvotefaceuk There is a way to handle this for both path resolution to be the same and get your assets into your function.

  • Put your function into a sub-directory under the folder you specified in your netlify.toml (i.e pdf-read.js would be put to `functions/pdf-read/pdf-read.js).
  • Include the assets in the functions/pdf-read folder
  • Change your path with require.resolve
    const contents = fs.readFileSync(require.resolve('./dummy.pdf'))

Working example in this repository to read a .txt file in the function under functions/read-file

Note: Remember, this is the target functions directory, not a source directory prior to a build. The example does not do a webpack functions build using netlify-lambda. That process would take a little more workflow to get the bundle and assets into the target location. As long as you are using nodejs javascript, this is a valid solution.

3 Likes

Moving the static files my function depended on to a subdirectory along with the function itself worked beautifully, thank you!

Although I didn’t need to use require.resolve to get the filepath. Simply using fs.readFileSync('dummy.pdf') worked fine. Are you sure this step is necessary?

Not needed in most cases. You should be good.

I’m trying to read files as shown in the example repo, but for some reason I keep getting cannot find module './build/__app.html.

/** get-html.js **/
exports.handler = async (event, context) => {
const template = fs.readFileSync(require.resolve('./build/__app.html'), 'utf8')
const script = fs.readFileSync(require.resolve('./build/bundle.js'), 'utf8')
...

The structure:

project/
  functions/
    get-html/
      build/
        __app.html
        bundle.js
      get-html.js //function being called

Am I doing something wrong?

Hi @jakobrosenberg,

Is your repo public? if so can you please share a link? Also are you zipping the function yourself or using zip it and ship it?

Functions of files in subdirectories of the main folder for function are invisible for build. So your solution currently doesn’t work.

Hey @step what does your file structure look like?

Following:
/dist hosting
/functions editing functions
/lambda build functions

To be honest, I just run netlify deploy. I assumed Netlify automatically zipped as necessary. This is not the case?

Also, is it possible to access files outside the respective function’s folder? Ie. Access files from the publish folder.

I created a working example to allow you to discover where your setup might not work:

It will work with netlify deploy.

Hope this helps you discover what is wrong about your setup.
Feel free to ask if anything about that setup is unclear.

2 Likes

Doesn’t work with Lambda. I have to resort to raw-loader to make it work.

hi @patarapolw - can you say a little more about how it doesn’t work? Are you getting an error message? What behaviour are you seeing? Thank you.

Hi, I’m having a similar issue.
I’m developing a lambda locally using this command (through Yarn):
netlify-lambda serve src/functions --config webpack.functions.js
(to add nodeExternals because I’m using Firebase).

I also have a .babelrc in my functions dir to make typescript work.
All is great both locally and on Netlify.

Now I would like to embark PDF files that I want to open with fs.readFileSync.

Initially, I had this error:

ENOENT: no such file or directory

Then I used require.resolve as suggested, but I got:

WARNING in ./pdf/coupon.pdf 1:0
Module parse failed: Unexpected token (1:0)

So I guessed Webpack was trying to load it as a source file, so I updated my webpack.functions.js like this:

const nodeExternals = require("webpack-node-externals")

module.exports = {
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.pdf$/i,
        use: [{ loader: "file-loader" }],
      },
    ],
  },
}

But now the error is:

{ Error: ENXIO: no such device or address, read
    at Object.readSync (fs.js:493:3)
    at tryReadSync (fs.js:332:20)
    at Object.readFileSync (fs.js:369:19)
    at Object.b [as handler] ([...]/functions/coupon.js:1:4032)
    at process._tickCallback (internal/process/next_tick.js:68:7) errno: -6, syscall: 'read', code: 'ENXIO' }

Any clue? :confused:

Edit: I realized that using a Babel loader meant the file would actually be read, so I don’t need to read it again, right? So I just imported the PDF:

const pdf = require("./assets/template.pdf")

(import works too)

but now the pdf-lib is failing with an error:

Error: Failed to parse PDF document (line:0 col:54 offset=27): No PDF header found

But if I make a Node script to open that PDF with that lib, it works…

Hmm, I am not sure if that is the best way to include a file but I am not a major functions user. We have a known working example of including a separate file shown here:

… can you try to emulate that pattern a bit to see if it works better?

1 Like

Are there any solutions or workarounds for accessing files from outside the functions directory, like the publish directory?

Since the ‘publish’ is essentially your site, you could use the env var URL (as mentioned here) to address the file on your deployed site. Note that your lambda function does not live in the same ‘machine’ as your netlify site, so you can’t access any files from your publish directory directly through some filesystem method.

Hope that answers your question.

1 Like

Thanks for your reply Dennis.

Another solution for me would be accessing files outside the root directory. Is that possible?

Ideally
Root Directory: scripts/now
Output Directory: ../../dist
Build Command npm run build

for a setup that looks like this

src/
dist/
scripts/
  now/
    api/
    build-for-now.js #Now specific build instructions
    now.json
    package.json #{"scripts": {"build": "node ./build-for-now.js"}}
    ...
  dev-server/
  service1/
  service2/