skip to Main Content

I was working on a Joke API for my SvelteKit project with Bun as the runtime and TypeScript as the programming language. My Joke API involves getting one random line from a text file containing 363 dad jokes. For that I obviously had to read the file. So I implemented that method and it worked splendidly…except for one part. The file reader couldn’t find the file. The file location I put in was ./jokes.txt, which was exactly where it was. However, the file reader said that it just can’t find it. I thought that because I was using Bun.file to read the file that it was causing issues because Bun is relatively new, so I tried readFileSync and that didn’t work either.

Here’s my code:

import { readFileSync } from "fs";

import { db } from "$lib/db";
import { sql } from "drizzle-orm";
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';

export async function GET() {
  let file = readFileSync("./jokes.txt", "utf-8");

  const jokes = file.split("n");

  const today = new Date();

  const jokecords = sqliteTable("jokecords", {
    id: integer("id").primaryKey(),
    joke: text("joke"),
    month: text("month"),
    day: text("day")
  });

  const matches = await db.select().from(jokecords).where(
    sql`month = ${today.getMonth().toString()} and day = ${today.getDay().toString()}`
  );

  let joke;

  if (matches.length > 0) {
    joke = matches[0];
  }
  else {
    joke = jokes[Math.floor(Math.random() * jokes.length)];
    await db.insert(jokecords).values({
      joke: joke,
      month: today.getMonth().toString(),
      day: today.getDay().toString()
    });
  }

  return {
    joke: joke
  }
}

and this is my file structure

.sveltekit
node_modules
src
  lib
    db.ts
  routes
    api
      +server.ts
      jokes.txt
      jokes-backup.txt
    +layout.svelte
    +page.svelte
  app.d.ts
  app.html
  app.pcss
static
.gitignore
.npmrc
bun.lockb
package.json
postcss.config.cjs
README.md
svelte.config.js
tailwind.config.cjs
tsconfig.json
vite.config.ts

2

Answers


  1. readFileSync can runs in an unespecified way because it depends on the relative path of the execution of code, not your current file. The best way to avoid this is to always compare the location of the code file with the execution runtime and resolve it, like:

    const path = require("path");
    
    fs.readFileSync(path.resolve(__dirname, "./jokes.txt"), "utf-8")
    
    Login or Signup to reply.
  2. If readFileSync throws an ENOENT there is point in insisting the file exists. It just doesn’t. And most of the time the error happens because the path you are using does not point to the location you think it does.

    The relative path ./jokes.txt does not point to the directory where the calling file is located but to the current working directory of the process. And that is — most certainly — not deep in your filestructure. Try logging a process.cwd() to see what the current working directory is.

    You could also use path.resolve('./jokes.txt') to see, where that relative path actually resolves to. And most certainly you won’t find a jokes.txt there.

    If you are really sure, you want to read a file in the same directory as your current file use __dirname

    readFileSync(path.join(__dirname, "jokes.txt"), "utf-8"); 
    

    And just some remarks unrelated to the issue

    • Using readFileSync in an async context kind of defeats the purpose of async because it blocks execution. There is an async fs promises API

    • I don’t know what db is, but

      const matches = await db.select().from(jokecords).where(
        sql`month = ${today.getMonth().toString()} and day = ${today.getDay().toString()}`
      );
      

      uses string templates to create a query, that’s considered bad style because it may open your code to SQL injections. Use parameterized queries instead.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search