T
TanStack•5w ago
exotic-emerald

Reading file in server function

The following code works in development but when I build the application the file being read is not found. The contents of the src/data directory are not copied into the build. What's the correct way to read a file with a server function? And where should the file be placed?
import { createServerFn } from '@tanstack/react-start';
import { readFile } from 'node:fs/promises';
import { join } from 'path';
import matter from 'gray-matter';
import { MarkdownMetadata } from '@/model/blog/markdown-metadata';

export const getAboutMarkdownServerFn = createServerFn({
method: 'GET',
}).handler(async ({ data }) => {
try {
const md = await readFile(`src/data/md/about.md`, 'utf-8');
const { data: mdData, content } = matter(md);
return {
...(mdData as MarkdownMetadata),
content: content.trimStart(),
};
} catch (err: any) {
if (err.code === 'ENOENT') {
// File not found
console.warn(`Markdown file not found for About page`);
return null; // Or throw a custom error, or return an empty object fallback
}
// Unexpected error
throw err;
}
});`
import { createServerFn } from '@tanstack/react-start';
import { readFile } from 'node:fs/promises';
import { join } from 'path';
import matter from 'gray-matter';
import { MarkdownMetadata } from '@/model/blog/markdown-metadata';

export const getAboutMarkdownServerFn = createServerFn({
method: 'GET',
}).handler(async ({ data }) => {
try {
const md = await readFile(`src/data/md/about.md`, 'utf-8');
const { data: mdData, content } = matter(md);
return {
...(mdData as MarkdownMetadata),
content: content.trimStart(),
};
} catch (err: any) {
if (err.code === 'ENOENT') {
// File not found
console.warn(`Markdown file not found for About page`);
return null; // Or throw a custom error, or return an empty object fallback
}
// Unexpected error
throw err;
}
});`
7 Replies
vicious-gold
vicious-gold•5w ago
it depends where you deploy to public dir will be copied over automatically anything else you need to copy yourself during build using e.g. some vite plugin
exotic-emerald
exotic-emeraldOP•4w ago
Thanks. This is on netlify. I've moved the files into the public/assets directory so I think this should be visible. If I look into that directory from the Netlify the file is certainly there. This is the file from the Netlify console: client-dist/assets/blog-posts.json However, the server function can't find the file at the following locations * client-dist/assets/blog-posts.json * ${process.cwd()}/client-dist/assets/blog-posts.json * public/assets/blog-posts.json * ${process.cwd()}/public/assets/blog-posts.json Any idea on how to reference this file useing the readFile() function?
vicious-gold
vicious-gold•4w ago
cc @Netlify (Partner)
exotic-emerald
exotic-emeraldOP•4w ago
To follow up on this, I've also added a Netlify.toml file which includes this:
[functions]
included_files = ['public/assets/**/*.md', 'public/assets/**/*.json', '!public/assets/**/*.@(jpeg|jpg|png)']
[functions]
included_files = ['public/assets/**/*.md', 'public/assets/**/*.json', '!public/assets/**/*.@(jpeg|jpg|png)']
Still no luck making this work. I created a little helper function to help me see what's been deployed on the server. This is what I have:
[
{
name: '___netlify-bootstrap.mjs',
isDirectory: false,
isFile: true
},
{
name: '___netlify-entry-point.mjs',
isDirectory: false,
isFile: true
},
{
name: '___netlify-metadata.json',
isDirectory: false,
isFile: true
},
{
name: '___netlify-telemetry.mjs',
isDirectory: false,
isFile: true
},
{ name: 'chunks', isDirectory: true, isFile: false },
{ name: 'main.mjs', isDirectory: false, isFile: true },
{ name: 'main.mjs.map', isDirectory: false, isFile: true },
{ name: 'node_modules', isDirectory: true, isFile: false },
{ name: 'package.json', isDirectory: false, isFile: true },
{ name: 'server.mjs', isDirectory: false, isFile: true }
]
[
{
name: '___netlify-bootstrap.mjs',
isDirectory: false,
isFile: true
},
{
name: '___netlify-entry-point.mjs',
isDirectory: false,
isFile: true
},
{
name: '___netlify-metadata.json',
isDirectory: false,
isFile: true
},
{
name: '___netlify-telemetry.mjs',
isDirectory: false,
isFile: true
},
{ name: 'chunks', isDirectory: true, isFile: false },
{ name: 'main.mjs', isDirectory: false, isFile: true },
{ name: 'main.mjs.map', isDirectory: false, isFile: true },
{ name: 'node_modules', isDirectory: true, isFile: false },
{ name: 'package.json', isDirectory: false, isFile: true },
{ name: 'server.mjs', isDirectory: false, isFile: true }
]
This shows the public/assets has not been copied across despite the netlify.toml entries.
correct-apricot
correct-apricot•3w ago
@Jingle Bells So the way this works is that files in your publish directory are deployed to the Netlify CDN, i.e. you can access them directly from a browser. This is where your css, js, html, etc. go. Netlify functions (and edge functions) don't have access to these. Each function is bundled and deployed as its own unit, which, by default, includes only the files determined to be needed by that function. To include additional files, you can use functions.included_files in your netlify.toml: https://docs.netlify.com/build/configure-builds/file-based-configuration/#functions. It looks to me like you've configured this correctly, but you have a second issue, unrelated to Netlify: fs.readFile('foo/bar') interprets foo/bar as a relative path from the cwd. When you try it locally you're running from the base of your project, but the deployed function runs from the function dir. So if your function is in functions/hello.ts you should be using ../foo/bar.
exotic-emerald
exotic-emeraldOP•3w ago
Thanks @Philippe Serhal Such a simple fix! 🙂 I also realised that given this is serverless environment I should probably be using blobs to do this rather than the file system. In reality, there are only about 100 text files. Maybe it's far simpler to just use the filesytem of the function. What do you suggest?
correct-apricot
correct-apricot•3w ago
Either approach seems fine to me! Using the FS is probably simplest for this case. You can also sort of use a hybrid approach: https://docs.netlify.com/build/data-and-storage/netlify-blobs/#file-based-uploads. Copy the files into .netlify/blobs/deploy in your build step and access them in your function with @netlify/blobs?

Did you find this page helpful?