Next.js: Next.js-API-Routen (und -Seiten) sollten das Lesen von Dateien unterstützen

Erstellt am 5. Aug. 2019  ·  87Kommentare  ·  Quelle: vercel/next.js

Featureanfrage

Bezieht sich Ihre Funktionsanfrage auf ein Problem? Bitte beschreiben.

Es ist derzeit nicht möglich, Dateien von API-Routen oder -Seiten zu lesen.

Beschreiben Sie die gewünschte Lösung

Ich möchte fs.readFile mit einem __dirname Pfad aufrufen und es "einfach funktionieren" lassen.

Dies sollte im Entwicklungs- und Produktionsmodus funktionieren.

Beschreiben Sie Alternativen, die Sie in Betracht gezogen haben

Dies muss möglicherweise in gewisser Weise in @zeit/webpack-asset-relocator-loader werden. Dieses Plugin verarbeitet diese Arten von Anforderungen.

Es ist jedoch keine Notwendigkeit. Ich wäre in Ordnung mit etwas, das _nur_ mit __dirname und __filename funktioniert (keine relativen oder cwd-basierten Pfade).

Zusätzlicher Kontext

Beispiel:

// pages/api/test.js
import fs from 'fs'
import path from 'path'

export default (req, res) => {
  const fileContent = fs.readFileSync(
    path.join(__dirname, '..', '..', 'package.json'), 
    'utf8'
  )
  // ...
}

Hinweis: Ich weiß, dass Sie das obige Beispiel ☝️ mit require betrügen können, aber das ist nicht der Punkt. 😄

story feature request

Hilfreichster Kommentar

Problemumgehung, die ich verwende:

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

und an der stelle brauchst du den weg

import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()

fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))

Ich weiß, dass dies nicht die Notwendigkeit löst, auf Dateien mit Pfaden relativ zur aktuellen Datei zu verweisen, aber dies löst meinen sehr verwandten Anwendungsfall (Lesen von Bilddateien aus einem /public/images Ordner).

Alle 87 Kommentare

Ich wollte das nur unterstreichen und versuchen, das Hochladen von Dateien mithilfe von API-Routen zu implementieren. Ich kann die Datei hochladen, muss dann aber wieder darauf zugreifen können, um sie in den S3-Bucket hochzuladen.

Ich unterstütze das! Außerdem ist es für die Nutzung meines Unternehmens sehr wichtig, Verzeichnisse lesen zu können, da wir unsere Daten wie Teammitglieder und Blog-Posts in einem Inhaltsverzeichnis speichern und nach einer Möglichkeit suchen, alle Dateien in dem Verzeichnis zu benötigen.

Der obige PR wird dies beheben! ☝️ 🙏

Wie wäre es mit fs.writeFile ist das möglich? Erstellen und speichern Sie beispielsweise eine JSON-Datei basierend auf einem Webhook, der auf einem /api/route gepostet wurde

Hey @marlonmarcello , das wird möglich sein. Bleib dran

Ist das schon gelöst?

Noch nicht, Sie können #8334 abonnieren

@huv1k Vielen Dank!

Gibt es eine Möglichkeit, diesen Schritt schneller voranzutreiben?

Hinweis: Wenn Sie TypeScript verwenden, können Sie bereits eine JSON-Datei direkt als Modul importieren (stellen Sie sicher, dass resolveJsonModule true in tsconfig.json ). Z.B:

import myJson from '../../../some/path/my.json';

Die Form des JSON-Objekts wird auch automatisch als Typ verwendet, also ist die automatische Vervollständigung wirklich nett.

Problemumgehung, die ich verwende:

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

und an der stelle brauchst du den weg

import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()

fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))

Ich weiß, dass dies nicht die Notwendigkeit löst, auf Dateien mit Pfaden relativ zur aktuellen Datei zu verweisen, aber dies löst meinen sehr verwandten Anwendungsfall (Lesen von Bilddateien aus einem /public/images Ordner).

Habe in der PR gesehen, dass sich dies ein wenig geändert hat - gibt es ein Update zu den aktuellen Plänen (oder nicht)? Klingt so, als gäbe es einige Strategien, die Sie nicht verfolgen möchten. Denken Sie daran, sie aufzulisten + warum können Mitwirkende dies versuchen?

Dies blockiert die Verwendung von Nexus mit Next.js. Es wäre schön, wenn dies noch einmal priorisiert würde.

Problemumgehung, die ich verwende:

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

und an der stelle brauchst du den weg

import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()

fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))

Ich weiß, dass dies nicht die Notwendigkeit löst, auf Dateien mit Pfaden relativ zur aktuellen Datei zu verweisen, aber dies löst meinen sehr verwandten Anwendungsfall (Lesen von Bilddateien aus einem /public/images Ordner).

Toller Kerl. Hat bei mir funktioniert.

Ich habe dafür die neue Methode getStaticProps verwendet (in #9524). Die Methode ist derzeit als instabil markiert, aber das Next.js-Team scheint gute Unterstützung bei der offiziellen Auslieferung zu geben.

z.B:

export async function unstable_getStaticProps() {
  const siteData = await import("../data/pages/siteData.json");
  const home = await import("../data/pages/home.json");

  return {
    props: { siteData, home }
  };
}

@ ScottSmith95 Haben Sie ein öffentliches Quellprojekt, in dem Sie dies verwenden? Neugierig, wie es aussehen würde.

Das Projekt ist noch nicht Open Source, aber ich freue mich, mehr von meiner Konfiguration zu teilen, wenn Sie weitere Fragen haben.

@ScottSmith95 Ich habe _alle_ die Fragen 😛

  1. Wo in Ihrem Projekt speichern Sie die Datendateien? (außen/innen src ?)
  2. Wie sieht eine next.js-Seitenkomponente aus, die sie verwendet?
  3. Handelt es sich nur um hartcodierte Pfade oder können Sie eine Datei basierend auf Pfadparametern laden?
  4. Wie funktioniert Build/Deployment, insbesondere wenn es sich nicht um hartcodierte Pfade handelt?

@Svish Wir speichern Datendateien in /data innerhalb unseres Projekts. (Seiten befinden sich in /pages, nicht in /src/prages.) Diese Seitenkomponente sieht so aus (Requisiten werden an die Home-Komponente gesendet, die der Standardexport ist):

// /pages/index.js
const Home = ({ siteData, home }) => {
  return (
    <>
      <Head>
        <meta name="description" content={siteData.siteDescription} />
        <meta name="og:description" content={siteData.siteDescription} />
        <meta
          name="og:image"
          content={getAbsoluteUrl(siteData.siteImage, constants.siteMeta.url)}
        />
      </Head>
      <section className={`container--fluid ${styles.hero}`}>
        <SectionHeader section={home.hero} heading="1">
          <div className="col-xs-12">
            <PrimaryLink
              href={home.hero.action.path}
              className={styles.heroAction}
            >
              {home.hero.action.text}
            </PrimaryLink>
          </div>
        </SectionHeader>
        <div className={styles.imageGradientOverlay}>
          <img src={home.hero.image.src} alt={home.hero.image.alt} />
        </div>
      </section>
    </>
  );
};

Bei fortgeschritteneren Seiten mit dynamischen Routen erfassen wir diese Daten wie folgt:

// /pages/studio/[member.js]
export async function unstable_getStaticProps({ params }) {
  const siteData = await import("../../data/pages/siteData.json");
  const member = await import(`../../data/team/${params.member}.json`);

  return {
    props: { siteData, member }
  };
}

Die Bereitstellung verläuft wirklich reibungslos, mit dynamischen Routen werden getStaticPaths() benötigt. Ich empfehle Ihnen, die Dokumentation dazu im RFC zu lesen, aber hier ist ein Beispiel dafür, wie wir damit umgehen, indem wir alle Daten unserer Teammitglieder sammeln und an Next.js übergeben.

// /pages/studio/[member.js]
export async function unstable_getStaticPaths() {
  const getSingleFileJson = async path => await import(`../../${path}`);

  // These utility functions come from `@asmallstudio/tinyutil` https://github.com/asmallstudio/tinyutil
  const directoryData = await getDirectory(
    "./data/team",
    ".json",
    getSingleFileJson,
    createSlugFromTitle
  );
  const directoryPaths = directoryData.reduce((pathsAccumulator, page) => {
    pathsAccumulator.push({
      params: {
        member: page.slug
      }
    });

    return pathsAccumulator;
  }, []);

  return directoryPaths;
}

@ ScottSmith95 Sieht vielversprechend aus! Ein paar Anschlussfragen, wenn Sie Zeit haben:

  1. Was tun Sie hier für die Generierung statischer Sites? Dh bei Verwendung von next export ?
  2. Habe ich es richtig verstanden, dass getStaticPaths eine Liste von Pfadparametern zurückgibt, die dann (von next) nacheinander für jedes Rendern in getStaticProps eingespeist wird?
  3. Können Sie getStaticProps ohne getStaticPaths , zum Beispiel für eine Seite ohne Parameter?
  4. Können Sie getStaticProps in _app ? Zum Beispiel, wenn Sie eine Site-weite Konfiguration haben, die Sie laden möchten, oder so ähnlich?

Was ist mit der Apis?? Diese Hooks sind für Seiten, aber was ist mit Apis?

Ich bin verwirrt. Ich konnte den _dirname als env-Variable in der nächsten config setzen. Daher konnte ich von der API auf das Dateisystem zugreifen, aber es funktionierte nur lokal. Nachdem ich es bis jetzt bereitgestellt habe, habe ich einen Fehler erhalten. Irgendwelche Ideen, warum es nach der Bereitstellung nicht funktioniert?

@josias-r Das Hauptproblem besteht normalerweise darin, dass die zu lesenden Dateien nicht in der Bereitstellung enthalten sind, aber es hängt davon ab, wie Sie sie einschließen und um welche Dateitypen es sich handelt ( js / json ist normalerweise in Ordnung, aber andere Dateitypen wie .jade erfordern alternative Wege, um mit ihm umzugehen, wie die Verwendung eines separaten @now/node Lambda/Deployment zum Lesen/Verarbeiten dieser Dateien).

Wenn Sie den Fehler genauer erklären können, kann Ihnen vielleicht jemand helfen.

@BrunoBernardino Es bezog sich tatsächlich auf JSON-Dateien in meinem src-Ordner. Aber es ist sogar die Methode fs.readdirSync(my_dirname_env_var) , die bereits bei der Bereitstellung fehlschlägt. Dieses Verzeichnis scheint nach der Bereitstellung also überhaupt nicht zu existieren. Folgendes erhalte ich, wenn ich versuche, auf den vollständigen Pfad zum Json über meine API zuzugreifen:

ERROR   Error: ENOENT: no such file or directory, open '/zeit/3fc37db3/src/content/somejsonfilethatexists.json'

Und wie ich bereits erwähnt habe, funktioniert dies lokal, wenn ich npm start baue und dann ausführe.

@josias-r Danke! Haben Sie versucht, fs.readdirSync stattdessen mit einem relativen Pfad (ohne Variablen) auszuführen (nur um die Bereitstellung zu debuggen)? Ich habe festgestellt, dass das normalerweise funktioniert, und wenn ja, können Sie dieses Stück Code (nur die Datei lesen, sie nirgendwo speichern) irgendwo in einem Initialisierungsprozess schreiben ( getInitialProps oder so), so dass die Deployment-Prozess erkennt, dass er diese Datei benötigt, und liest sie dann mit der var im eigentlichen Code/Logik weiter. Es ist nicht ordentlich, aber es funktioniert, bis dies unterstützt wird. Ich glaube, dass auch die Verwendung von __dirname in einigen Fällen funktioniert.

@BrunoBernardino Ich konnte einen Dateibaum ausgehend vom root-relativen Pfad ./ erstellen. Was ich bekam, war das folgende JSON (ohne die aufgeführten Knotenmodule):

{
  "path": "./",
  "name": ".",
  "type": "folder",
  "children": [
    {
      "path": ".//.next",
      "name": ".next",
      "type": "folder",
      "children": [
        {
          "path": ".//.next/serverless",
          "name": "serverless",
          "type": "folder",
          "children": [
            {
              "path": ".//.next/serverless/pages",
              "name": "pages",
              "type": "folder",
              "children": [
                {
                  "path": ".//.next/serverless/pages/api",
                  "name": "api",
                  "type": "folder",
                  "children": [
                    {
                      "path": ".//.next/serverless/pages/api/posts",
                      "name": "posts",
                      "type": "folder",
                      "children": [
                        {
                          "path": ".//.next/serverless/pages/api/posts/[...id].js",
                          "name": "[...id].js",
                          "type": "file"
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "path": ".//node_modules",
      "name": "node_modules",
      "type": "folder",
      "children": ["alot of children here ofc"]
    },
    { "path": ".//now__bridge.js", "name": "now__bridge.js", "type": "file" },
    {
      "path": ".//now__launcher.js",
      "name": "now__launcher.js",
      "type": "file"
    }
  ]
}

Ihre JSON-Datei scheint dort zu fehlen, haben Sie versucht, sie über den Code einzubinden, wie ich oben vorgeschlagen habe? Das Hauptproblem ist, dass die Optimierungen, die das Deployment ausführt, nicht immer dynamische Pfade aufnehmen, glaube ich, also hat es in der Vergangenheit für mich funktioniert, einen statischen Pfad zu erzwingen (nicht unbedingt für den eigentlichen Code, der ausgeführt wird, aber um sicherzustellen, dass die relevanten Dateien sind inklusive). Macht das Sinn?

@BrunoBernardino Ich bin auf eine Nicht-API-Lösung import() verwenden. Ich wollte es einfach nicht so machen, weil es hackig erscheint, aber es macht im Wesentlichen dasselbe, was mein API-Endpunkt getan hätte.
... Ich habe versucht, die Datei in den statischen Ordner zu legen, aber auch das hat nicht funktioniert. Aber ich hoffe, dass der Zugriff auf das Dateisystem in Zukunft möglich sein wird.

Ich musste auch auf hackige Lösungen zurückgreifen, aber hoffentlich wird dies bald landen und mehr Leute werden Next als produktionsbereit ansehen, da diese Anwendungsfälle "wie erwartet" unterstützt werden.

Problemumgehung, die ich verwende:

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

und an der stelle brauchst du den weg

import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()

fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))

Ich weiß, dass dies nicht die Notwendigkeit löst, auf Dateien mit Pfaden relativ zur aktuellen Datei zu verweisen, aber dies löst meinen sehr verwandten Anwendungsfall (Lesen von Bilddateien aus einem /public/images Ordner).

Toller Kerl. Hat bei mir funktioniert.

Es funktioniert perfekt bei der lokalen Entwicklung, obwohl es bei der Bereitstellung auf now nicht zu funktionieren scheint.

ENOENT: no such file or directory, open '/zeit/41c233e5/public/images/my-image.png'
    at Object.openSync (fs.js:440:3)
    at Object.readFileSync (fs.js:342:35)
    at getEmailImage (/var/task/.next/serverless/pages/api/contact/demo.js:123:52)
    at module.exports.7gUS.__webpack_exports__.default (/var/task/.next/serverless/pages/api/contact/demo.js:419:87)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:42:9) {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: '/zeit/41c233e5/public/images/my-image.png'
}

Ich verstehe, dass der öffentliche Ordner in die Route verschoben wird, also habe ich versucht, ihn zu zwingen, in der Produktion im Basisordner zu suchen, aber immer noch das gleiche Ergebnis erhalten:

ENOENT: no such file or directory, open '/zeit/5fed13e9/images/my-image.png'
    at Object.openSync (fs.js:440:3)
    at Object.readFileSync (fs.js:342:35)
    at getEmailImage (/var/task/.next/serverless/pages/api/contact/demo.js:124:52)
    at module.exports.7gUS.__webpack_exports__.default (/var/task/.next/serverless/pages/api/contact/demo.js:331:87)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:42:9) {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: '/zeit/5fed13e9/images/my-image.png'
}

@PaulPCIO das Problem, das Sie dort haben, liegt daran, dass es sich nicht um eine .json , .js - oder .ts Datei handelt. Die Dateien unter /public werden auf einem CDN, aber nicht auf dem Lambda (AFAIK) "bereitgestellt". In diesem Fall benötigen Sie also entweder ein dediziertes Lambda ( @now/node ) mit includeFiles , oder, wenn Sie nur diese einzelne Datei benötigen, konvertieren Sie sie in base64 und verwenden Sie diese als var (in einer dedizierten Datei oder nicht).

Danke @BrunoBernardino , so viel erwartet, ich werde die Methode base64 verwenden

Es ist eine Auflösung für den __dirname in der bereitgestellten Umgebung??

@NicolasHz kannst du das

@BrunoBernardino Wenn ich mir die letzten Kommentare, einschließlich meiner, _dirname in der nächsten Konfiguration" im Deployment nicht funktioniert. Sogar mit js- und JSON-Dateien. Zumindest für die Bereitstellung von now gilt dies wahrscheinlich nicht für benutzerdefinierte Bereitstellungen.

@BrunoBernardino Ich kann einige Variablen nicht verwenden, die auf den lokalen Pfad in der bereitgestellten Umgebung verweisen. __dirname Es ist undefiniert, sobald es bereitgestellt wurde, und ich kann keine Datei aus meinen APIs-Skripten lesen.

Verstanden @NicolasHz . Ja, Sie müssen auf eine der oben genannten Lösungen zurückgreifen, je nachdem, welche Art von Datei Sie lesen/zugreifen müssen.

Nur zur Bestätigung, dass config.js bei Bereitstellungen nicht funktioniert.

Problemumgehung, die ich verwende:

# next.config.js
module.exports = {
  env: {
    PROJECT_DIRNAME: __dirname,
  },
}

und in der API-Definition, wo ich den Pfad benötige (allPosts-Ordner enthält alle Blogs im Markdown-Format und befindet sich im Projektstamm)

import fs from 'fs'
import { join } from 'path'

const postsDirectory = join(process.env.PROJECT_DIRNAME, 'allPosts')

Es funktioniert perfekt auf die lokale Entwicklung.
Aber es gibt diesen Fehler, wenn es jetzt an zeit bereitgestellt wird.

[POST] /api/postsApi
11:00:13:67
Status:
500
Duration:
8.1ms
Memory Used:
76 MB
ID:
kxq8t-1585546213659-5c3393750f30
User Agent:
axios/0.19.2
{
  fields: [ 'title', 'date', 'slug', 'author', 'coverImage', 'excerpt' ],
  page: 1
}
2020-03-30T05:30:13.688Z    572075eb-4a7a-47de-be16-072a9f7005f7    ERROR   Error: ENOENT: no such file or directory, scandir '/zeit/1cc63678/allPosts'
    at Object.readdirSync (fs.js:871:3)
    at getPostSlugs (/var/task/.next/serverless/pages/api/postsApi.js:306:52)
    at module.exports.fZHd.__webpack_exports__.default (/var/task/.next/serverless/pages/api/postsApi.js:253:86)
    at apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:48:15)
    at processTicksAndRejections (internal/process/task_queues.js:97:5) {
  errno: -2,
  syscall: 'scandir',
  code: 'ENOENT',
  path: '/zeit/1cc63678/allPosts'
}

@sjcodebook wie @BrunoQuaresma sagte, dieser Workaround funktioniert nur lokal. Ich verwende immer noch eine separate @now/node Bereitstellung für Lambdas, um auf das Dateisystem zuzugreifen, und rufe diese Datei über eine Anfrage von der App selbst auf (oder erzeuge ein beliebiges statisches Ergebnis, das ich vor der Bereitstellung benötige). Irgendwie verrückt, aber es funktioniert.

Hallo @BrunoBernardino... Meinst du ein separates Projekt mit einem benutzerdefinierten Knotenserver?

Ich verstehe jedoch nicht, warum es eine Einstellung für " includeFiles " gibt, wenn es dann unmöglich ist, darauf zuzugreifen 🤔

@valse es kann sich im selben Projekt befinden. Hier ist ein Ausschnitt meiner now.json :

{
  "builds": [
    {
      "src": "next.config.js",
      "use": "@now/next"
    },
    {
      "src": "lambdas/**/*.ts",
      "use": "@now/node",
      "config": {
        "includeFiles": ["email-templates/**"]
      }
    }
  ],
  "routes": [
    {
      "src": "/lambdas/(.+)",
      "dest": "/lambdas/$1.ts"
    }
  ]
}

Auf diese Weise kann ich sie wie folgt anrufen:

await ky.post(`${hostUrl}/lambdas/email?token=${someToken}`);

aus einer nächsten API-Seite, vorausgesetzt, ich habe eine lambdas/email.ts Datei, die das Senden von E-Mails und das Lesen von Vorlagendateien wie pug .

Ich hoffe das hilft!

Außerdem funktioniert "includeFiles" nur für @now/node (vielleicht andere, aber nicht @now/next )

@BrunoBernardino sieht so aus, als ob es bei Verwendung von node Funktionen jetzt kein ESM lesen kann!

Folgendes passiert, wenn ich versuche, eine Liste von mdx-Seiten zu importieren:

Code

import { NextApiRequest, NextApiResponse } from 'next'
import { promises as fs } from 'fs'
import { join } from 'path'
const { readdir } = fs

export default async (req: NextApiRequest, res: NextApiResponse) => {
  const postFiles = await readdir(join(process.cwd(), 'pages', 'blog'))

  const postNames: string[] = postFiles.filter((page: string) => page !== 'index.tsx')

  const posts = []

  for (const post of postNames) {
    const mod = await import(`../pages/blog/${post}`)

    posts.push({ ...mod, link: post.slice(0, post.indexOf('.')) })
  }

  res.status(200).json([])
}

den fehler bekomme ich:

export const title = 'My new website!'
^^^^^^

SyntaxError: Unexpected token 'export'

@talentlessguy Ich sondern bin nur ein zufriedener Kunde. Scheint so, als ob das besser für ihren Kundensupport geeignet wäre, da ich allein in diesem Ausschnitt ein paar potenzielle Probleme sehe:

  1. Vielleicht möchten Sie einfach nichts oder __dirname anstelle von process.cwd() für den Basispfad verwenden. Letzteres habe ich nicht in Lambdas verwendet, aber die anderen, daher bin ich mir nicht sicher, ob das ein Problem ist oder nicht
  2. Sie importieren NextApiRequest und NextApiResponse als Typen, aber dies sollte ab @now/node" laufen, oder? Die Typen sollten also wie folgt importiert werden:
import { NowRequest, NowResponse } from '@now/node';
  1. Sie importieren/lesen von pages/... aber schließen Sie sie über includeFiles ? Wie sieht dein now.json aus?

@BrunoBernardino

Ich kann __dirname weil es immer / , process.cwd() stattdessen den echten Pfad anzeigt

Ich habe deine Fixes akzeptiert und es hat funktioniert:

lambdas/posts.ts

import { NowResponse, NowRequest } from '@now/node'
import { promises as fs } from 'fs'
import { join } from 'path'
const { readdir } = fs

export default async (req: NowRequest, res: NowResponse) => {
  const postFiles = await readdir(join(process.cwd(), 'pages', 'blog'))

  const postNames: string[] = postFiles.filter((page: string) => page !== 'index.tsx')

  const posts = []

  for (const post of postNames) {
    const mod = await import(`../pages/blog/${post}`)

    posts.push({ ...mod, link: post.slice(0, post.indexOf('.')) })
  }

  res.status(200).json([])
}

jetzt.json

{
  "builds": [
    {
      "src": "next.config.js",
      "use": "@now/next"
    },
    {
      "src": "lambdas/**/*.ts",
      "use": "@now/node",
      "config": {
        "includeFiles": ["pages/blog/*.mdx"]
      }
    }
  ],
  "routes": [
    {
      "src": "/lambdas/(.+)",
      "dest": "/lambdas/$1.ts"
    }
  ]
}

Fehler bekomme ich:

import Meta from '../../components/Article/Meta.tsx'
^^^^^^

SyntaxError: Cannot use import statement outside a module

Sieht so aus, als ob die Typoskript-Knotenfunktion .mdx als Modul behandeln kann:(

Alles klar, Sie haben das Problem anscheinend gefunden. Versuchen Sie, den Dateiinhalt zu lesen und zu analysieren, anstatt ihn direkt zu importieren. Ich habe noch nie gesehen, dass ein Import wie dieser funktioniert, und es scheint etwas zu sein, das nur mit etwas Babel-Magie funktionieren würde, die Sie auch gerne anstelle von einfachem TS verwenden können.

@BrunoBernardino du hast recht, aber es ist nicht klar ... Ich habe das Ziel auf esnext und das Modul auf esnext gesetzt, es sollte in der Lage sein, alles zu importieren ... aber irgendwie tut es das nicht

Es hat sowieso nichts mit dem Problem zu tun, werde es irgendwo googeln

Kein Problem. Ein paar Tipps könnten in https://mdxjs.com/advanced/typescript und https://mdxjs.com/getting-started/webpack enthalten sein, wodurch die Bereitstellung von @now/node möglicherweise angepasst werden muss benutze es. Jedenfalls sollte ihre Unterstützung hilfreich sein.

irgendeine Bewegung dazu? Es wäre großartig, HTML-E-Mail-Vorlagen für die Verwendung in API-Routen einbinden zu können. Im Moment füge ich sie in JS-Dateien ein, aber ich bin kein besonderer Fan dieses Hacks.

Ein weiterer Hack besteht darin, den webpack raw-loader zu verwenden, um sie in js einzubetten.

yarn add --dev raw-loader
const templates = {
    verify: require("raw-loader!../template/email/verify.hbs").default,
};

Verwenden Sie dann templates.verify als Zeichenfolge.

Es gibt ein Problem mit next-i18next, das mit diesem zusammenzuhängen scheint ( vercel/vercel#4271 ). Grundsätzlich fügt now die .json Dateien, die sich in /public/static/locales/ in die serverlose Funktion ein. Kann jemand eine Problemumgehung bereitstellen, bis die hier besprochene Funktion als nächstes hinzugefügt wird?

@borispoehland haben Sie die oben genannten Import-/

@borispoehland haben Sie die oben genannten Import-/

@BrunoBernardino Ich weiß nicht, welchen genauen Kommentar du meinst.

Können Sie mir ein Beispiel geben, wie alle .json Dateien in public/static/locales irgendwie in die serverlose Funktion importiert werden können? Und wo geht das (in welcher Datei)?

Ich verwende next (wie Sie bereits erwähnt haben, ist includeFiles nicht mit @now/next kompatibel, idk, wenn dies Auswirkungen auf mein Problem hat).

Außerdem, weil next-i18next für mich eine Art Blackbox ist (daher möchte ich die Dateien nicht von dort importieren), suche ich nach einer Möglichkeit, sie vollständig zu importieren, damit next-i18next dies direkt kann darauf zugreifen (in anderen Kommentaren oben wurde manchmal nur das PROJECT_DIRNAME innerhalb des next.config.json und der Import musste manuell durchgeführt werden. Dies versuche ich nicht zu erreichen). Wie in vercel/vercel#4271 möchte ich nur, dass now meine .json Dateien irgendwie in die serverlose Funktion bringt.

@borispoehland in _any_ file in pages/api (oder das wird dort von einem aufgerufen), mach etwas wie https://github.com/vercel/next.js/issues/8251#issuecomment -544008976

Beim Importieren brauchen Sie nichts zu tun. Der Punkt ist, dass die Webpack-Vercel-Ausführungen dann sehen, dass diese Dateien eingeschlossen werden müssen, und es sollte funktionieren.

Ich hoffe das ergibt Sinn.

@borispoehland in _any_ file in pages/api (oder das wird dort von einem aufgerufen), mach etwas wie #8251 (Kommentar)

Beim Importieren brauchen Sie nichts zu tun. Der Punkt ist, dass die Webpack-Vercel-Ausführungen dann sehen, dass diese Dateien eingeschlossen werden müssen, und es sollte funktionieren.

Ich hoffe das ergibt Sinn.

@BrunoBernardino Das Problem bei diesem Ansatz ist, dass ich viele JSON-Dateien habe. Den Import für jede Datei manuell durchzuführen ist etwas umständlich. Gibt es eine einfachere Möglichkeit, now zu sagen: "Hey, bitte alle JSON-Dateien in diesem Verzeichnis rekursiv abholen"? Danke im Voraus

Bearbeiten: Sogar das manuelle Importieren von json Dateien führt zu demselben Fehler wie zuvor. Ich werde dafür ein neues Thema eröffnen, denke ich

Ich habe ein neues Thema für mein Problem eröffnet , falls jemand Interesse hat, an der Diskussion teilzunehmen. Danke fürs Erste ,

Eine andere Option / Problemumgehung, um die Möglichkeit zu aktivieren, __dirname wie Sie es normalerweise erwarten würden, besteht darin, die Webpack-Konfiguration anzupassen.

Standardmäßig wird Webpack verschiedene Node-Globals mit Polyfills aliasen, es sei denn, Sie weisen es an, dies nicht zu tun:
https://webpack.js.org/configuration/node/
Und die Webpack-Standardeinstellungen sind, __dirname und __filename Ruhe zu lassen, dh sie nicht polyfill zu lassen und sie von Node wie gewohnt behandeln zu lassen.

Die Webpack-Konfiguration von Next.js verwendet/reflektiert jedoch nicht die Webpack-Standardeinstellungen https://github.com/vercel/next.js/blob/bb6ae2648ddfb65a810edf6ff90a86201d52320c/packages/next/build/webpack-config.ts#L661 -L663

Alles in allem habe ich das folgende benutzerdefinierte Next-Config-Plugin verwendet, um die Webpack-Konfiguration anzupassen.

WICHTIG: Dies funktioniert für meinen Anwendungsfall. Es wurde weder in einer Vielzahl von Umgebungen/Konfigurationen noch mit allen Next.js-Unit-/Integrationstests getestet. Die Verwendung kann unbeabsichtigte Nebenwirkungen in Ihrer Umgebung haben.
Außerdem kann Next bestimmte Gründe haben, die Webpack-Standardeinstellungen für __dirname und __filename . Auch hier kann der folgende Code unbeabsichtigte Nebenwirkungen haben und sollte mit Vorsicht verwendet werden.

Außerdem wurde das folgende Plugin für die Verwendung mit dem next-compose-plugins Paket entwickelt: https://github.com/cyrilwanner/next-compose-plugins

Sollte aber auch als normales Plugin funktionieren: https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config

const withCustomWebpack = (nextCfg) => {
  return Object.assign({}, nextCfg, {
    webpack(webpackConfig, options) {
      // We only want to change the `server` webpack config.
      if (options.isServer) {
        // set `__dirname: false` and/or `__filename: false` here to align with webpack defaults:
        // https://webpack.js.org/configuration/node/
        Object.assign(webpackConfig.node, { __dirname: false });
      }

      if (typeof nextCfg.webpack === 'function') {
        return nextCfg.webpack(webpackConfig, options);
      }

      return webpackConfig;
    },
  });
};

Ich habe die Lösung von @jkjustjoshing implementiert , und obwohl sie lokal hervorragend funktioniert, funktioniert sie nicht, wenn ich die App in Vercel bereitstelle.

Ich bekomme folgenden Fehler:

Error: GraphQL error: ENOENT: no such file or directory, open '/vercel/37166432/public/ts-data.csv'

Mein Code:

const content = await fs.readFile(
  path.join(serverRuntimeConfig.PROJECT_ROOT, "./public/ts-data.csv")
);

Hier ein Link zur Datei: https://github.com/bengrunfeld/trend-viewer/blob/master/pages/api/graphql-data.js

@bengrunfeld ja, deine Lösung funktioniert nur lokal.

Ich hatte in letzter Zeit ein ähnliches Problem (wollte eine Datei in einer API-Route lesen) und die Lösung war einfacher als erwartet.

Versuchen Sie es mit path.resolve('./public/ts-data.csv')

@borispoehland Vielen Dank!! Deine Lösung hat wunderbar funktioniert!

@bengrunfeld kein Problem, ich habe es auch durch Zufall herausgefunden ( @BrunoBernardino ;)). Es ist die Lösung für jedermanns Problem hier, denke ich.

Bitte beachten Sie, dass Sie immer noch next.config.js festlegen müssen. Ich habe die Datei gelöscht, nachdem ich gesehen habe, dass die Lösung von @borispoehland funktioniert, und einen ähnlichen Fehler erhalten.

Dann habe ich es auf die obige Lösung von

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Bitte beachten Sie, dass Sie immer noch next.config.js festlegen müssen. Ich habe es entfernt, nachdem ich gesehen habe, dass die Lösung von @borispoehland funktioniert, und einen ähnlichen Fehler erhalten.

Ich habe es auf die obige Lösung von

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

@bengrünfeld Wirklich? Vielleicht verwenden Sie an einer anderen Stelle im Code noch den PROJECT_ROOT Ansatz, da es in meinem Projekt ohne ihn funktioniert. Wie sieht der Fehler aus?

Wie schreibe ich readFile Bereitstellung in Vercel ein

Demo-Repository, wo es nicht funktioniert: https://github.com/mathdroid/blog-fs-demo

https://blog-fs-demo.vercel.app/

@mathdroid versuche readFile und readdir in die Funktionen getStaticProps bzw. getStaticPaths , andernfalls könnte der Code im Browser ausgeführt werden.

Das Importieren von fs sollte obendrein in Ordnung sein.

Lassen Sie uns wissen, wie das funktioniert.

@borispoehland Danke für die wunderbare Lösung. Hätte nicht erwartet, dass path.resolve() bis /public sowohl lokal als auch auf Vercel funktionieren würde :eyes:! Du bist mein Retter für den Tag. :+1:

@borispoehland Ich habe Ihre Lösung in einer serverlosen Funktion ausprobiert, aber immer noch:
ENOENT: keine solche Datei oder kein solches Verzeichnis, öffnen Sie '/var/task/public/posts.json'

const postsFile = resolve('./public/posts.json');

const updateCache = async (posts: IPost[]): Promise<IPost[]> => {
    postCache = posts;
    fs.writeFileSync(postsFile, JSON.stringify(postCache)); // <====
    return postCache;
}

Ich habe es mit unserem ohne next.config.js versucht

module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Vielleicht funktioniert Ihre Lösung nicht auf serverlosen Funktionen?

@borispoehland Ich habe Ihre Lösung in einer serverlosen Funktion ausprobiert, aber immer noch:
ENOENT: keine solche Datei oder kein solches Verzeichnis, öffnen Sie '/var/task/public/posts.json'

const postsFile = resolve('./public/posts.json');

const updateCache = async (posts: IPost[]): Promise<IPost[]> => {
    postCache = posts;
    fs.writeFileSync(postsFile, JSON.stringify(postCache)); // <====
    return postCache;
}

Ich habe es mit unserem ohne next.config.js versucht

module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Vielleicht funktioniert Ihre Lösung nicht auf serverlosen Funktionen?

Ich weiß nicht, warum es bei dir nicht funktioniert... Sorry

Ok, ich habe es mit @bengrunfeld- Code zum Lesen
[Fehler: EROFS: schreibgeschütztes Dateisystem, öffne '/var/task/public/posts.json']
Also keine Möglichkeit, einen Cache zu aktualisieren, um zu viele Datenbankaufrufe zu vermeiden :(

@neckaros haben Sie versucht, meinen Ansatz zu verwenden, um eine andere Datei als .json zu lesen, z. B. eine .jpg Datei?

Ok, ich habe es mit @bengrunfeld- Code zum Lesen

[Fehler: EROFS: schreibgeschütztes Dateisystem, öffne '/var/task/public/posts.json']

Also keine Möglichkeit, einen Cache zu aktualisieren, um zu viele Datenbankaufrufe zu vermeiden :(

@neckaros Sie sollten in der Lage sein, von S3 (oder einem anderen externen Dateisystem) zu schreiben und zu lesen, aber ich verwende normalerweise Redis für schnelle, zwischengespeicherte Dinge, die flüchtig sein können. https://redislabs.com hält es "serverlos", und ich habe produktionsbereite Codebeispiele in https://nextjs-boilerplates.brn.sh, wenn Sie möchten.

@borispoehland ich konnte lesen, aber nicht schreiben von der dienstlosen Funktion. Aber am Ende funktionierte es, indem ich meinen Cache in den inkrementellen Builds aktualisierte (erneut validierte), anstatt neuen Inhalt hinzuzufügen. Was meiner Meinung nach kein schlechtes Muster ist. Danke für Ihre Hilfe!

@BrunoBernardino danke ich werde mir das mal anschauen. Ich suche wirklich nach einer völlig kostenlosen Bastlerlösung, die nicht kaputt geht, sobald Sie ein paar Benutzer haben :)

Ich suche wirklich nach einer völlig kostenlosen Bastlerlösung, die nicht kaputt geht, sobald Sie ein paar Benutzer haben :)

Kopiere das. RedisLabs und Vercel haben das für mich getan. 💯

Nach einigem Suchen habe ich das Schreiben von Dateien mit dem erweiterten Betriebssystempaket geschafft ...

import { tmpdir } from "os";
const doc = new PDFDocument()
const pdfPath = path.join(tmpdir(), `${store.id}${moment().format('YYYYMMDD')}.pdf`)
const writeStream = doc.pipe(fs.createWriteStream(pdfPath)

Das Lesen einer Datei funktioniert mit der @subwaymatch- Lösung
const logoPath = path.resolve('./public/logo.png')

Nach einigem Suchen habe ich das Schreiben von Dateien mit dem erweiterten Betriebssystempaket geschafft ...

import { tmpdir } from "os";
const doc = new PDFDocument()
const pdfPath = path.join(tmpdir(), `${store.id}${moment().format('YYYYMMDD')}.pdf`)
const writeStream = doc.pipe(fs.createWriteStream(pdfPath)

Das Lesen einer Datei funktioniert mit der @subwaymatch- Lösung
const logoPath = path.resolve('./public/logo.png')

Schön, können Sie den Inhalt dieser Datei zurücklesen? Ist das Verzeichnis zugänglich und dauerhaft?

@marklundin Mit einer Funktion namens tmpdir bezweifle ich, dass sie dauerhaft ist, aber wenn das funktioniert, wäre es gut zu wissen, wie temporär tmpdir tatsächlich ist, ja...

Irgendwelche Updates dazu? Ich frage mich, warum es in getInitialProps funktioniert, aber nicht in API-Routen 🤷‍♂️

Mein aktueller Workaround

const data = await import(`../../../../data/de/my-nice-file.json`);
res.json(data.default);

habe dieses Problem derzeit auch in API-Routen

habe dieses Problem derzeit auch in API-Routen

Hier gibt es ein paar funktionierende Lösungen, welches Problem haben Sie konkret?

Ich habe Mühe, dies trotz Vorschlägen aus diesem Thread zum Laufen zu bringen. Mein Anwendungsfall ist, dass ich eine Anleitung schreibe und den Quellcode für die Komponente neben der Komponente selbst anzeigen möchte. Meine Methode dafür besteht darin, fs zu verwenden, um die jsx-Datei der Komponente in getServerSideProps zu laden und den Zeichenfolgenwert des Dateiinhalts als Requisite zu übergeben.

Ich war überglücklich darüber, dass es vor Ort funktioniert, aber als ich es dann einsetzen wollte, war die Freude verflogen :(

Siehe: https://github.com/ElGoorf/i18next-guide/blob/fix-example-components/pages/plurals.jsx

@ElGoorf Ihr Problem ist, dass sich die public Dateien an einem Rand befinden und die Funktionen sich auf einem Lambda befinden. @vercel/next lässt includeFiles @vercel/next immer noch nicht zu, also wäre der einfachste Weg, um es zum Laufen zu bringen, eine lambda Funktion zu verwenden.

Hier ist ein Beispielcode, der anderen hier geholfen hat: https://github.com/vercel/next.js/issues/8251#issuecomment -614220305

Danke @BrunoBernardino Ich wusste nicht, dass ich das "x versteckte Elemente laden mehr ..." verpasst hatte und dachte, ich würde verrückt werden,

Leider hatte ich mit Ihrer Lösung zu kämpfen, da ich zum ersten Mal von Edge/Lambda höre. Ich fand jedoch, @balthild näher an dem war, was ich ursprünglich gesucht hatte, bevor ich die node.fs-Methode ausprobierte: https:/ /github.com/vercel/next.js/issues/8251#issuecomment -634829189

Groß! Hast du es zum Laufen gebracht? Oder hast du immer noch Probleme?

Ich bin mir nicht sicher, ob Vercel diese Terminologie überhaupt verwendet, aber mit Edge meine ich CDN, von dem aus statische Dateien bereitgestellt werden, und mit Lambda meine ich die "Backend" -Funktionen, die von den API-Routen aufgerufen werden, die wie AWS Lambda-Funktionen isoliert sind .

Hey,

Gibt es ein Update zum Schreiben in Dateien mit next.js auf Vercel? Ich kann ohne Probleme lesen. Verwenden der const logoPath = path.resolve('./public/logo.png')

Ich versuche, die Datei public/sitemap.xml zu überschreiben (aufgrund der Größenbeschränkungen von Vercel). Ich kann sie nur fehlerfrei als statische Datei im öffentlichen Ordner zurückgeben. Ich habe die Sitemap zuvor mit zlib implementiert und die Antwort gestreamt, aber es scheint zu warten, bis der Stream fertig ist, und gibt sie dann zurück. Dies trifft nicht auf den Größenbeschränkungsfehler, ist aber leider sehr langsam. Ich bin für alle Vorschläge offen, die Leute haben könnten. Die Sitemap wird aus einem API-Aufruf an ein separates Backend erstellt und muss regelmäßig aktualisiert werden.

Dinge, die ich versucht habe:

  • XML komprimieren und streamen - funktioniert aber sehr langsam.
  • Erstellen und Zurückgeben einer Sitemap aus einer API-Funktion, leider erreicht dies die Größenbeschränkung.
  • Das Lesen einer statischen XML-Datei aus einem öffentlichen Ordner (funktioniert) unabhängig von der Größe, ist jedoch nicht aktualisierbar.
  • Testen Sie das Schreiben in diese Datei, funktioniert nicht. Testschreiben in eine beliebige Datei/einen Ordner funktioniert nicht
  • Testen Sie die Rückgabe einer statischen XML-Datei von der Funktion "api", Größenfehler. Das funktioniert lokal.
  • Testen Sie, ob eine statische XML-Datei von "page" getServerSideProp-Größenfehler zurückgegeben wird. Das funktioniert lokal.
  • Würde mich über irgendwelche Ideen freuen?

Hey @emomooney , ich kann mir nicht vorstellen, dass Vercel jemals erlaubt, Dateien in eine Funktion zu schreiben (auch nicht zum Caching), da der Hauptvorteil von Serverless seine Zustandslosigkeit ist, und das würde ihm einen Zustand hinzufügen müssen dafür Edge/cdn verwenden.

Ich habe die Sitemap zuvor mit zlib implementiert und die Antwort gestreamt, aber es scheint zu warten, bis der Stream fertig ist, und gibt sie dann zurück.

Ich bin gespannt, ob Sie diese Langsamkeit nur bei nachfolgenden Anrufen oder nur beim ersten beim Kaltstart erlebt haben? Ich kann mir vorstellen, dass dies ein API-Aufruf an Vercel über eine next.js-API-Funktion oder ein dediziertes Lambda war, ähnlich wie ich es hier mache .

Wenn es war und es immer noch zu langsam war, ist Ihr "separates Backend" außerhalb von Vercel? Wenn ja, können Sie es möglicherweise verwenden, um eine sitemap.xml Datei und vercel --prod in eine Domäne zu erstellen das robots.txt um die Sitemap mit einer anderen Domain/Subdomain zu verknüpfen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen