Server-Funktionen

Was sind Server-Funktionen?

Server-Funktionen ermöglichen es Ihnen, Logik zu definieren, die fast überall (sogar auf dem Client) aufgerufen werden kann, aber **nur** auf dem Server ausgeführt wird. Tatsächlich unterscheiden sie sich nicht wesentlich von einer API-Route, aber mit einigen wichtigen Unterschieden:

  • Sie haben keine stabile öffentliche URL.
  • Sie können von überall in Ihrer Anwendung aufgerufen werden, einschließlich Loadern, Hooks, Komponenten, Server-Routen usw.

Sie ähneln jedoch regulären API-Routen insofern, als dass

  • Sie Zugriff auf den Request-Kontext haben und somit Header lesen, Cookies setzen und vieles mehr können.
  • Sie auf sensible Informationen wie Umgebungsvariablen zugreifen können, ohne diese an den Client weiterzugeben.
  • Sie für jede Art von serverseitiger Logik verwendet werden können, wie z. B. das Abrufen von Daten aus einer Datenbank, das Senden von E-Mails oder die Interaktion mit anderen Diensten.
  • Sie jeden beliebigen Wert zurückgeben können, einschließlich primitiver Werte, JSON-serialisierbarer Objekte und sogar roher Response-Objekte.
  • Sie Fehler auslösen können, einschließlich Weiterleitungen und "nicht gefunden", die automatisch vom Router behandelt werden können.

Wie unterscheiden sich Server-Funktionen von "Solid Server Functions"?

  • TanStack Server-Funktionen sind nicht an ein bestimmtes Frontend-Framework gebunden und können mit jedem Frontend-Framework oder auch ohne eines verwendet werden.
  • TanStack Server-Funktionen basieren auf Standard-HTTP-Anfragen und können beliebig oft aufgerufen werden, ohne von seriellen Ausführung-Engpässen betroffen zu sein.

Wie funktionieren sie?

Server-Funktionen können überall in Ihrer Anwendung definiert werden, müssen aber auf der obersten Ebene einer Datei definiert werden. Sie können in Ihrer gesamten Anwendung aufgerufen werden, einschließlich Loadern, Hooks usw. Traditionell ist dieses Muster als Remote Procedure Call (RPC) bekannt, aber aufgrund der isomeren Natur dieser Funktionen bezeichnen wir sie als Server-Funktionen.

  • Im Server-Bundle bleibt die Logik der Server-Funktionen unverändert. Es muss nichts weiter unternommen werden, da sie sich bereits am richtigen Ort befinden.
  • Auf dem Client werden Server-Funktionen entfernt; sie existieren nur auf dem Server. Alle Aufrufe der Server-Funktion auf dem Client werden durch eine fetch-Anfrage an den Server ersetzt, um die Server-Funktion auszuführen und die Antwort zurück an den Client zu senden.

Server-Funktions-Middleware

Server-Funktionen können Middleware verwenden, um Logik, Kontext, gängige Operationen, Voraussetzungen und vieles mehr zu teilen. Um mehr über die Middleware von Server-Funktionen zu erfahren, lesen Sie bitte die Informationen dazu im Middleware-Leitfaden.

Server-Funktionen definieren

Wir möchten dem tRPC-Team für die Inspiration des Server-Funktionsdesigns von TanStack Start und die Anleitung bei der Implementierung danken. Wir lieben (und empfehlen) die Verwendung von tRPC für API-Routen so sehr, dass wir darauf bestanden haben, dass Server-Funktionen die gleiche erstklassige Behandlung und Entwicklererfahrung erhalten. Vielen Dank!

Server-Funktionen werden mit der Funktion createServerFn aus dem Paket @tanstack/solid-start definiert. Diese Funktion nimmt ein optionales options-Argument entgegen, um Konfigurationen wie die HTTP-Methode und den Antworttyp festzulegen, und ermöglicht es Ihnen, vom Ergebnis abgeleitet Dinge wie den Body der Server-Funktion, Eingabevalidierung, Middleware usw. zu definieren. Hier ist ein einfaches Beispiel:

tsx
// getServerTime.ts
import { createServerFn } from '@tanstack/solid-start'

export const getServerTime = createServerFn().handler(async () => {
  // Wait for 1 second
  await new Promise((resolve) => setTimeout(resolve, 1000))
  // Return the current time
  return new Date().toISOString()
})
// getServerTime.ts
import { createServerFn } from '@tanstack/solid-start'

export const getServerTime = createServerFn().handler(async () => {
  // Wait for 1 second
  await new Promise((resolve) => setTimeout(resolve, 1000))
  // Return the current time
  return new Date().toISOString()
})

Konfigurationsoptionen

Beim Erstellen einer Server-Funktion können Sie Konfigurationsoptionen angeben, um ihr Verhalten anzupassen.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const getData = createServerFn({
  method: 'GET', // HTTP method to use
  response: 'data', // Response handling mode
}).handler(async () => {
  // Function implementation
})
import { createServerFn } from '@tanstack/solid-start'

export const getData = createServerFn({
  method: 'GET', // HTTP method to use
  response: 'data', // Response handling mode
}).handler(async () => {
  // Function implementation
})

Verfügbare Optionen

method

Gibt die HTTP-Methode für die Server-Funktionsanfrage an.

tsx
method?: 'GET' | 'POST'
method?: 'GET' | 'POST'

Standardmäßig verwenden Server-Funktionen GET, wenn nichts anderes angegeben ist.

response

Steuert, wie Antworten verarbeitet und zurückgegeben werden.

tsx
response?: 'data' | 'full' | 'raw'
response?: 'data' | 'full' | 'raw'
  • 'data' (Standard): Analysiert automatisch JSON-Antworten und gibt nur die Daten zurück.
  • 'full': Gibt ein Antwortobjekt mit Ergebnisdaten, Fehlerinformationen und Kontext zurück.
  • 'raw': Gibt das rohe Response-Objekt direkt zurück und ermöglicht gestreamte Antworten und benutzerdefinierte Header.

Wo kann ich Server-Funktionen aufrufen?

  • Aus serverseitigem Code
  • Aus clientseitigem Code
  • Aus anderen Server-Funktionen

Parameter akzeptieren

Server-Funktionen akzeptieren einen einzelnen Parameter, der verschiedene Typen haben kann:

  • Standard-JavaScript-Typen
    • string
    • number
    • boolean
    • null
    • Array
    • Objekt
  • FormData
  • ReadableStream (von einem der obigen Typen)
  • Promise (von einem der obigen Typen)

Hier ist ein Beispiel für eine Server-Funktion, die einen einfachen String-Parameter akzeptiert.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const greet = createServerFn({
  method: 'GET',
})
  .validator((data: string) => data)
  .handler(async (ctx) => {
    return `Hello, ${ctx.data}!`
  })

greet({
  data: 'John',
})
import { createServerFn } from '@tanstack/solid-start'

export const greet = createServerFn({
  method: 'GET',
})
  .validator((data: string) => data)
  .handler(async (ctx) => {
    return `Hello, ${ctx.data}!`
  })

greet({
  data: 'John',
})

Laufende Eingabevalidierung / Typsicherheit

Server-Funktionen können so konfiguriert werden, dass sie ihre Eingabedaten zur Laufzeit validieren und gleichzeitig Typsicherheit hinzufügen. Dies ist nützlich, um sicherzustellen, dass die Eingabe den richtigen Typ hat, bevor die Server-Funktion ausgeführt wird, und um freundlichere Fehlermeldungen bereitzustellen.

Dies geschieht mit der Methode validator. Sie akzeptiert jede Eingabe, die an die Server-Funktion übergeben wird. Der Wert (und Typ), den Sie aus dieser Funktion zurückgeben, wird zur Eingabe, die an den eigentlichen Handler der Server-Funktion übergeben wird.

Validatoren integrieren sich auch nahtlos mit externen Validatoren, wenn Sie etwas wie Zod verwenden möchten.

Grundlegende Validierung

Hier ist ein einfaches Beispiel für eine Server-Funktion, die den Eingabeparameter validiert.

tsx
import { createServerFn } from '@tanstack/solid-start'

type Person = {
  name: string
}

export const greet = createServerFn({ method: 'GET' })
  .validator((person: unknown): Person => {
    if (typeof person !== 'object' || person === null) {
      throw new Error('Person must be an object')
    }

    if ('name' in person && typeof person.name !== 'string') {
      throw new Error('Person.name must be a string')
    }

    return person as Person
  })
  .handler(async ({ data }) => {
    return `Hello, ${data.name}!`
  })
import { createServerFn } from '@tanstack/solid-start'

type Person = {
  name: string
}

export const greet = createServerFn({ method: 'GET' })
  .validator((person: unknown): Person => {
    if (typeof person !== 'object' || person === null) {
      throw new Error('Person must be an object')
    }

    if ('name' in person && typeof person.name !== 'string') {
      throw new Error('Person.name must be a string')
    }

    return person as Person
  })
  .handler(async ({ data }) => {
    return `Hello, ${data.name}!`
  })

Verwendung einer Validierungsbibliothek

Validierungsbibliotheken wie Zod können wie folgt verwendet werden:

tsx
import { createServerFn } from '@tanstack/solid-start'

import { z } from 'zod'

const Person = z.object({
  name: z.string(),
})

export const greet = createServerFn({ method: 'GET' })
  .validator((person: unknown) => {
    return Person.parse(person)
  })
  .handler(async (ctx) => {
    return `Hello, ${ctx.data.name}!`
  })

greet({
  data: {
    name: 'John',
  },
})
import { createServerFn } from '@tanstack/solid-start'

import { z } from 'zod'

const Person = z.object({
  name: z.string(),
})

export const greet = createServerFn({ method: 'GET' })
  .validator((person: unknown) => {
    return Person.parse(person)
  })
  .handler(async (ctx) => {
    return `Hello, ${ctx.data.name}!`
  })

greet({
  data: {
    name: 'John',
  },
})

Typsicherheit

Da Server-Funktionen die Netzwerkgrenze überschreiten, ist es wichtig sicherzustellen, dass die an sie übergebenen Daten nicht nur den richtigen Typ haben, sondern auch zur Laufzeit validiert werden. Dies ist besonders wichtig, wenn es um Benutzereingaben geht, da diese unvorhersehbar sein können. Um sicherzustellen, dass Entwickler ihre E/A-Daten validieren, sind Typen von der Validierung abhängig. Der Rückgabetyp der Funktion validator ist die Eingabe für den Handler der Server-Funktion.

tsx
import { createServerFn } from '@tanstack/solid-start'

type Person = {
  name: string
}

export const greet = createServerFn({ method: 'GET' })
  .validator((person: unknown): Person => {
    if (typeof person !== 'object' || person === null) {
      throw new Error('Person must be an object')
    }

    if ('name' in person && typeof person.name !== 'string') {
      throw new Error('Person.name must be a string')
    }

    return person as Person
  })
  .handler(
    async ({
      data, // Person
    }) => {
      return `Hello, ${data.name}!`
    },
  )

function test() {
  greet({ data: { name: 'John' } }) // OK
  greet({ data: { name: 123 } }) // Error: Argument of type '{ name: number; }' is not assignable to parameter of type 'Person'.
}
import { createServerFn } from '@tanstack/solid-start'

type Person = {
  name: string
}

export const greet = createServerFn({ method: 'GET' })
  .validator((person: unknown): Person => {
    if (typeof person !== 'object' || person === null) {
      throw new Error('Person must be an object')
    }

    if ('name' in person && typeof person.name !== 'string') {
      throw new Error('Person.name must be a string')
    }

    return person as Person
  })
  .handler(
    async ({
      data, // Person
    }) => {
      return `Hello, ${data.name}!`
    },
  )

function test() {
  greet({ data: { name: 'John' } }) // OK
  greet({ data: { name: 123 } }) // Error: Argument of type '{ name: number; }' is not assignable to parameter of type 'Person'.
}

Inferenz

Server-Funktionen leiten ihre Eingabe- und Ausgabetypen aus der Eingabe der Funktion validator und dem Rückgabewert der Funktion handler ab. Tatsächlich kann der von Ihnen definierte validator eigene separate Ein-/Ausgabetypen haben, was nützlich sein kann, wenn Ihr Validator Transformationen an den Eingabedaten durchführt.

Um dies zu veranschaulichen, werfen wir einen Blick auf ein Beispiel mit der Validierungsbibliothek zod.

tsx
import { createServerFn } from '@tanstack/solid-start'
import { z } from 'zod'

const transactionSchema = z.object({
  amount: z.string().transform((val) => parseInt(val, 10)),
})

const createTransaction = createServerFn()
  .validator(transactionSchema)
  .handler(({ data }) => {
    return data.amount // Returns a number
  })

createTransaction({
  data: {
    amount: '123', // Accepts a string
  },
})
import { createServerFn } from '@tanstack/solid-start'
import { z } from 'zod'

const transactionSchema = z.object({
  amount: z.string().transform((val) => parseInt(val, 10)),
})

const createTransaction = createServerFn()
  .validator(transactionSchema)
  .handler(({ data }) => {
    return data.amount // Returns a number
  })

createTransaction({
  data: {
    amount: '123', // Accepts a string
  },
})

Nicht validierte Inferenz

Obwohl wir dringend empfehlen, eine Validierungsbibliothek zur Validierung Ihrer Netzwerk-E/A-Daten zu verwenden, möchten Sie vielleicht aus irgendeinem Grund Ihre Daten **nicht** validieren, aber trotzdem Typsicherheit haben. Um dies zu tun, stellen Sie Typinformationen für die Server-Funktion über eine Identitätsfunktion als validator bereit, die die Eingabe und/oder Ausgabe auf die korrekten Typen typisiert.

tsx
import { createServerFn } from '@tanstack/solid-start'

type Person = {
  name: string
}

export const greet = createServerFn({ method: 'GET' })
  .validator((d: Person) => d)
  .handler(async (ctx) => {
    return `Hello, ${ctx.data.name}!`
  })

greet({
  data: {
    name: 'John',
  },
})
import { createServerFn } from '@tanstack/solid-start'

type Person = {
  name: string
}

export const greet = createServerFn({ method: 'GET' })
  .validator((d: Person) => d)
  .handler(async (ctx) => {
    return `Hello, ${ctx.data.name}!`
  })

greet({
  data: {
    name: 'John',
  },
})

JSON-Parameter

Server-Funktionen können JSON-serialisierbare Objekte als Parameter akzeptieren. Dies ist nützlich, um komplexe Datenstrukturen an den Server zu übergeben.

tsx
import { createServerFn } from '@tanstack/solid-start'

type Person = {
  name: string
  age: number
}

export const greet = createServerFn({ method: 'GET' })
  .validator((data: Person) => data)
  .handler(async ({ data }) => {
    return `Hello, ${data.name}! You are ${data.age} years old.`
  })

greet({
  data: {
    name: 'John',
    age: 34,
  },
})
import { createServerFn } from '@tanstack/solid-start'

type Person = {
  name: string
  age: number
}

export const greet = createServerFn({ method: 'GET' })
  .validator((data: Person) => data)
  .handler(async ({ data }) => {
    return `Hello, ${data.name}! You are ${data.age} years old.`
  })

greet({
  data: {
    name: 'John',
    age: 34,
  },
})

FormData-Parameter

Server-Funktionen können FormData-Objekte als Parameter akzeptieren.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const greetUser = createServerFn({ method: 'POST' })
  .validator((data) => {
    if (!(data instanceof FormData)) {
      throw new Error('Invalid form data')
    }
    const name = data.get('name')
    const age = data.get('age')

    if (!name || !age) {
      throw new Error('Name and age are required')
    }

    return {
      name: name.toString(),
      age: parseInt(age.toString(), 10),
    }
  })
  .handler(async ({ data: { name, age } }) => {
    return `Hello, ${name}! You are ${age} years old.`
  })

// Usage
function Test() {
  return (
    <form
      onSubmit={async (event) => {
        event.preventDefault()
        const formData = new FormData(event.currentTarget)
        const response = await greetUser({ data: formData })
        console.log(response)
      }}
    >
      <input name="name" />
      <input name="age" />
      <button type="submit">Submit</button>
    </form>
  )
}
import { createServerFn } from '@tanstack/solid-start'

export const greetUser = createServerFn({ method: 'POST' })
  .validator((data) => {
    if (!(data instanceof FormData)) {
      throw new Error('Invalid form data')
    }
    const name = data.get('name')
    const age = data.get('age')

    if (!name || !age) {
      throw new Error('Name and age are required')
    }

    return {
      name: name.toString(),
      age: parseInt(age.toString(), 10),
    }
  })
  .handler(async ({ data: { name, age } }) => {
    return `Hello, ${name}! You are ${age} years old.`
  })

// Usage
function Test() {
  return (
    <form
      onSubmit={async (event) => {
        event.preventDefault()
        const formData = new FormData(event.currentTarget)
        const response = await greetUser({ data: formData })
        console.log(response)
      }}
    >
      <input name="name" />
      <input name="age" />
      <button type="submit">Submit</button>
    </form>
  )
}

Server-Funktionskontext

Zusätzlich zu dem einzelnen Parameter, den Server-Funktionen akzeptieren, können Sie auch auf den serverseitigen Request-Kontext innerhalb jeder Server-Funktion zugreifen, indem Sie Dienstprogramme von @tanstack/solid-start/server verwenden. Intern verwenden wir das h3-Paket von Unjs, um plattformübergreifende HTTP-Anfragen durchzuführen.

Es stehen viele Kontextfunktionen zur Verfügung, z. B. für:

  • Zugriff auf den Request-Kontext
  • Zugriff auf/Setzen von Headern
  • Zugriff auf/Setzen von Sessions/Cookies
  • Festlegen von Response-Statuscodes und -nachrichten
  • Umgang mit Multi-Part-Formulardaten
  • Lesen/Setzen benutzerdefinierter serverseitiger Kontext-Eigenschaften

Eine vollständige Liste der verfügbaren Kontextfunktionen finden Sie unter allen verfügbaren h3-Methoden oder inspizieren Sie den @tanstack/start-server-core Quellcode.

Hier sind zunächst einige Beispiele:

Zugriff auf den Request-Kontext

Verwenden wir die Funktion getWebRequest, um von innerhalb einer Server-Funktion auf die Anfrage selbst zuzugreifen.

tsx
import { createServerFn } from '@tanstack/solid-start'
import { getWebRequest } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    const request = getWebRequest()

    console.log(request.method) // GET

    console.log(request.headers.get('User-Agent')) // Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3
  },
)
import { createServerFn } from '@tanstack/solid-start'
import { getWebRequest } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    const request = getWebRequest()

    console.log(request.method) // GET

    console.log(request.headers.get('User-Agent')) // Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3
  },
)

Zugriff auf Header

Verwenden Sie die Funktion getHeaders, um von innerhalb einer Server-Funktion auf alle Header zuzugreifen.

tsx
import { createServerFn } from '@tanstack/solid-start'
import { getHeaders } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    console.log(getHeaders())
    // {
    //   "accept": "*/*",
    //   "accept-encoding": "gzip, deflate, br",
    //   "accept-language": "en-US,en;q=0.9",
    //   "connection": "keep-alive",
    //   "host": "localhost:3000",
    //   ...
    // }
  },
)
import { createServerFn } from '@tanstack/solid-start'
import { getHeaders } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    console.log(getHeaders())
    // {
    //   "accept": "*/*",
    //   "accept-encoding": "gzip, deflate, br",
    //   "accept-language": "en-US,en;q=0.9",
    //   "connection": "keep-alive",
    //   "host": "localhost:3000",
    //   ...
    // }
  },
)

Sie können auch einzelne Header mit der Funktion getHeader aufrufen.

tsx
import { createServerFn } from '@tanstack/solid-start'
import { getHeader } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    console.log(getHeader('User-Agent')) // Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3
  },
)
import { createServerFn } from '@tanstack/solid-start'
import { getHeader } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    console.log(getHeader('User-Agent')) // Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3
  },
)

Werte zurückgeben

Server-Funktionen können verschiedene Arten von Werten zurückgeben:

  • Primitive
  • JSON-serialisierbare Objekte
  • redirect-Fehler (können auch ausgelöst werden)
  • notFound-Fehler (können auch ausgelöst werden)
  • Rohe Response-Objekte

Primitive und JSON zurückgeben

Um primitive oder JSON-serialisierbare Objekte zurückzugeben, geben Sie einfach den Wert aus der Server-Funktion zurück.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    return new Date().toISOString()
  },
)

export const getServerData = createServerFn({ method: 'GET' }).handler(
  async () => {
    return {
      message: 'Hello, World!',
    }
  },
)
import { createServerFn } from '@tanstack/solid-start'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    return new Date().toISOString()
  },
)

export const getServerData = createServerFn({ method: 'GET' }).handler(
  async () => {
    return {
      message: 'Hello, World!',
    }
  },
)

Standardmäßig gehen Server-Funktionen davon aus, dass jedes Nicht-Response-Objekt entweder ein primitiver oder ein JSON-serialisierbarer Wert ist.

Mit benutzerdefinierten Headern antworten

Um mit benutzerdefinierten Headern zu antworten, können Sie die Funktion setHeader verwenden.

tsx
import { createServerFn } from '@tanstack/solid-start'
import { setHeader } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    setHeader('X-Custom-Header', 'value')
    return new Date().toISOString()
  },
)
import { createServerFn } from '@tanstack/solid-start'
import { setHeader } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    setHeader('X-Custom-Header', 'value')
    return new Date().toISOString()
  },
)

Mit benutzerdefinierten Statuscodes antworten

Um mit einem benutzerdefinierten Statuscode zu antworten, können Sie die Funktion setResponseStatus verwenden.

tsx
import { createServerFn } from '@tanstack/solid-start'
import { setResponseStatus } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    setResponseStatus(201)
    return new Date().toISOString()
  },
)
import { createServerFn } from '@tanstack/solid-start'
import { setResponseStatus } from '@tanstack/solid-start/server'

export const getServerTime = createServerFn({ method: 'GET' }).handler(
  async () => {
    setResponseStatus(201)
    return new Date().toISOString()
  },
)

Rohe Response-Objekte zurückgeben

Um ein rohes Response-Objekt zurückzugeben, geben Sie ein Response-Objekt aus der Server-Funktion zurück und setzen Sie response: 'raw'.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const getServerTime = createServerFn({
  method: 'GET',
  response: 'raw',
}).handler(async () => {
  // Read a file from s3
  return fetch('https://example.com/time.txt')
})
import { createServerFn } from '@tanstack/solid-start'

export const getServerTime = createServerFn({
  method: 'GET',
  response: 'raw',
}).handler(async () => {
  // Read a file from s3
  return fetch('https://example.com/time.txt')
})

Die Option `response: 'raw'` ermöglicht auch gestreamte Antworten und vieles mehr.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const streamEvents = createServerFn({
  method: 'GET',
  response: 'raw',
}).handler(async ({ signal }) => {
  // Create a ReadableStream to send chunks of data
  const stream = new ReadableStream({
    async start(controller) {
      // Send initial response immediately
      controller.enqueue(new TextEncoder().encode('Connection established\n'))

      let count = 0
      const interval = setInterval(() => {
        // Check if the client disconnected
        if (signal.aborted) {
          clearInterval(interval)
          controller.close()
          return
        }

        // Send a data chunk
        controller.enqueue(
          new TextEncoder().encode(
            `Event ${++count}: ${new Date().toISOString()}\n`,
          ),
        )

        // End after 10 events
        if (count >= 10) {
          clearInterval(interval)
          controller.close()
        }
      }, 1000)

      // Ensure we clean up if the request is aborted
      signal.addEventListener('abort', () => {
        clearInterval(interval)
        controller.close()
      })
    },
  })

  // Return a streaming response
  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
    },
  })
})
import { createServerFn } from '@tanstack/solid-start'

export const streamEvents = createServerFn({
  method: 'GET',
  response: 'raw',
}).handler(async ({ signal }) => {
  // Create a ReadableStream to send chunks of data
  const stream = new ReadableStream({
    async start(controller) {
      // Send initial response immediately
      controller.enqueue(new TextEncoder().encode('Connection established\n'))

      let count = 0
      const interval = setInterval(() => {
        // Check if the client disconnected
        if (signal.aborted) {
          clearInterval(interval)
          controller.close()
          return
        }

        // Send a data chunk
        controller.enqueue(
          new TextEncoder().encode(
            `Event ${++count}: ${new Date().toISOString()}\n`,
          ),
        )

        // End after 10 events
        if (count >= 10) {
          clearInterval(interval)
          controller.close()
        }
      }, 1000)

      // Ensure we clean up if the request is aborted
      signal.addEventListener('abort', () => {
        clearInterval(interval)
        controller.close()
      })
    },
  })

  // Return a streaming response
  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
    },
  })
})

Die Option response: 'raw' ist besonders nützlich für:

  • Streaming-APIs, bei denen Daten inkrementell gesendet werden.
  • Server-Sent Events.
  • Long-Polling-Antworten.
  • Benutzerdefinierte Content-Typen und Binärdaten.

Fehler auslösen

Abgesehen von speziellen redirect- und notFound-Fehlern können Server-Funktionen beliebige benutzerdefinierte Fehler auslösen. Diese Fehler werden serialisiert und als JSON-Antwort mit einem 500er-Statuscode an den Client gesendet.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  throw new Error('Something went wrong!')
})

// Usage
function Test() {
  try {
    await doStuff()
  } catch (error) {
    console.error(error)
    // {
    //   message: "Something went wrong!",
    //   stack: "Error: Something went wrong!\n    at doStuff (file:///path/to/file.ts:3:3)"
    // }
  }
}
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  throw new Error('Something went wrong!')
})

// Usage
function Test() {
  try {
    await doStuff()
  } catch (error) {
    console.error(error)
    // {
    //   message: "Something went wrong!",
    //   stack: "Error: Something went wrong!\n    at doStuff (file:///path/to/file.ts:3:3)"
    // }
  }
}

Abbrechen

Auf dem Client können Server-Funktionsaufrufe über ein AbortSignal abgebrochen werden. Auf dem Server benachrichtigt ein AbortSignal, wenn die Anfrage vor Abschluss der Ausführung geschlossen wurde.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const abortableServerFn = createServerFn().handler(
  async ({ signal }) => {
    return new Promise<string>((resolve, reject) => {
      if (signal.aborted) {
        return reject(new Error('Aborted before start'))
      }
      const timerId = setTimeout(() => {
        console.log('server function finished')
        resolve('server function result')
      }, 1000)
      const onAbort = () => {
        clearTimeout(timerId)
        console.log('server function aborted')
        reject(new Error('Aborted'))
      }
      signal.addEventListener('abort', onAbort, { once: true })
    })
  },
)

// Usage
function Test() {
  const controller = new AbortController()
  const serverFnPromise = abortableServerFn({
    signal: controller.signal,
  })
  await new Promise((resolve) => setTimeout(resolve, 500))
  controller.abort()
  try {
    const serverFnResult = await serverFnPromise
    console.log(serverFnResult) // should never get here
  } catch (error) {
    console.error(error) // "signal is aborted without reason"
  }
}
import { createServerFn } from '@tanstack/solid-start'

export const abortableServerFn = createServerFn().handler(
  async ({ signal }) => {
    return new Promise<string>((resolve, reject) => {
      if (signal.aborted) {
        return reject(new Error('Aborted before start'))
      }
      const timerId = setTimeout(() => {
        console.log('server function finished')
        resolve('server function result')
      }, 1000)
      const onAbort = () => {
        clearTimeout(timerId)
        console.log('server function aborted')
        reject(new Error('Aborted'))
      }
      signal.addEventListener('abort', onAbort, { once: true })
    })
  },
)

// Usage
function Test() {
  const controller = new AbortController()
  const serverFnPromise = abortableServerFn({
    signal: controller.signal,
  })
  await new Promise((resolve) => setTimeout(resolve, 500))
  controller.abort()
  try {
    const serverFnResult = await serverFnPromise
    console.log(serverFnResult) // should never get here
  } catch (error) {
    console.error(error) // "signal is aborted without reason"
  }
}

Server-Funktionen aus Route-Lebenszyklen aufrufen

Server-Funktionen können normal aus Route-loaders, beforeLoads oder anderen Router-gesteuerten APIs aufgerufen werden. Diese APIs sind ausgestattet, um Fehler, Weiterleitungen und nicht gefundene Elemente, die von Server-Funktionen ausgelöst werden, automatisch zu verarbeiten.

tsx
import { getServerTime } from './getServerTime'

export const Route = createFileRoute('/time')({
  loader: async () => {
    const time = await getServerTime()

    return {
      time,
    }
  },
})
import { getServerTime } from './getServerTime'

export const Route = createFileRoute('/time')({
  loader: async () => {
    const time = await getServerTime()

    return {
      time,
    }
  },
})

Server-Funktionen aus Hooks und Komponenten aufrufen

Server-Funktionen können redirects oder notFounds auslösen, und obwohl nicht erforderlich, wird empfohlen, diese Fehler abzufangen und entsprechend zu behandeln. Um dies zu erleichtern, exportiert das Paket @tanstack/solid-start einen Hook useServerFn, der verwendet werden kann, um Server-Funktionen an Komponenten und Hooks zu binden.

tsx
import { useServerFn } from '@tanstack/solid-start'
import { useQuery } from '@tanstack/solid-query'
import { getServerTime } from './getServerTime'

export function Time() {
  const getTime = useServerFn(getServerTime)

  const timeQuery = useQuery({
    queryKey: 'time',
    queryFn: () => getTime(),
  })
}
import { useServerFn } from '@tanstack/solid-start'
import { useQuery } from '@tanstack/solid-query'
import { getServerTime } from './getServerTime'

export function Time() {
  const getTime = useServerFn(getServerTime)

  const timeQuery = useQuery({
    queryKey: 'time',
    queryFn: () => getTime(),
  })
}

Server-Funktionen anderswo aufrufen

Bei der Verwendung von Server-Funktionen beachten Sie, dass Weiterleitungen und nicht gefundene Elemente, die sie auslösen, nur automatisch behandelt werden, wenn sie von

  • Route-Lebenszyklen
  • Komponenten, die den useServerFn-Hook verwenden

verwendet werden. Für andere Verwendungspunkte müssen Sie diese Fälle manuell behandeln.

Weiterleitungen

Server-Funktionen können einen redirect-Fehler auslösen, um den Benutzer zu einer anderen URL weiterzuleiten. Dies ist nützlich für die Behandlung von Authentifizierung, Autorisierung oder anderen Szenarien, in denen Sie den Benutzer auf eine andere Seite weiterleiten müssen.

  • Während SSR werden Weiterleitungen behandelt, indem eine 302-Antwort mit dem neuen Speicherort an den Client gesendet wird.
  • Auf dem Client werden Weiterleitungen automatisch vom Router aus einem Route-Lebenszyklus oder einer Komponente, die den Hook useServerFn verwendet, behandelt. Wenn Sie eine Server-Funktion von irgendwo anders aufrufen, werden Weiterleitungen nicht automatisch behandelt.

Um eine Weiterleitung auszulösen, können Sie die Funktion redirect verwenden, die aus dem Paket @tanstack/solid-router exportiert wird.

tsx
import { redirect } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  // Redirect the user to the home page
  throw redirect({
    to: '/',
  })
})
import { redirect } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  // Redirect the user to the home page
  throw redirect({
    to: '/',
  })
})

Weiterleitungen können alle gleichen Optionen wie router.navigate, useNavigate() und <Link>-Komponenten nutzen. Sie können also auch gerne Folgendes übergeben:

  • Pfadparameter (Path Params)
  • Suchparameter (Search Params)
  • Hash

Weiterleitungen können auch den Statuscode der Antwort festlegen, indem sie eine status-Option übergeben.

tsx
import { redirect } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  // Redirect the user to the home page with a 301 status code
  throw redirect({
    to: '/',
    status: 301,
  })
})
import { redirect } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  // Redirect the user to the home page with a 301 status code
  throw redirect({
    to: '/',
    status: 301,
  })
})

Sie können auch zu einem externen Ziel weiterleiten, indem Sie href verwenden.

tsx
import { redirect } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

export const auth = createServerFn({ method: 'GET' }).handler(async () => {
  // Redirect the user to the auth provider
  throw redirect({
    href: 'https://authprovider.com/login',
  })
})
import { redirect } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

export const auth = createServerFn({ method: 'GET' }).handler(async () => {
  // Redirect the user to the auth provider
  throw redirect({
    href: 'https://authprovider.com/login',
  })
})

⚠️ Verwenden Sie **nicht** die Funktion sendRedirect von @tanstack/solid-start/server, um Soft-Redirects aus Server-Funktionen zu senden. Dies sendet den Redirect über den Location-Header und erzwingt eine vollständige Seiten-Hard-Navigation auf dem Client.

Redirect-Header

Sie können auch benutzerdefinierte Header bei einer Weiterleitung festlegen, indem Sie eine headers-Option übergeben.

tsx
import { redirect } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  // Redirect the user to the home page with a custom header
  throw redirect({
    to: '/',
    headers: {
      'X-Custom-Header': 'value',
    },
  })
})
import { redirect } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  // Redirect the user to the home page with a custom header
  throw redirect({
    to: '/',
    headers: {
      'X-Custom-Header': 'value',
    },
  })
})

Nicht gefunden

Beim Aufruf einer Server-Funktion aus einem Route-Lebenszyklus loader oder beforeLoad kann ein spezieller notFound-Fehler ausgelöst werden, um dem Router anzuzeigen, dass die angeforderte Ressource nicht gefunden wurde. Dies ist nützlicher als ein einfacher 404-Statuscode, da es Ihnen ermöglicht, eine benutzerdefinierte 404-Seite zu rendern oder den Fehler auf benutzerdefinierte Weise zu behandeln. Wenn `notFound` aus einer Server-Funktion außerhalb eines Route-Lebenszyklus ausgelöst wird, wird er nicht automatisch behandelt.

Um `notFound` auszulösen, können Sie die Funktion notFound verwenden, die aus dem Paket @tanstack/solid-router exportiert wird.

tsx
import { notFound } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

const getStuff = createServerFn({ method: 'GET' }).handler(async () => {
  // Randomly return a not found error
  if (Math.random() < 0.5) {
    throw notFound()
  }

  // Or return some stuff
  return {
    stuff: 'stuff',
  }
})

export const Route = createFileRoute('/stuff')({
  loader: async () => {
    const stuff = await getStuff()

    return {
      stuff,
    }
  },
})
import { notFound } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

const getStuff = createServerFn({ method: 'GET' }).handler(async () => {
  // Randomly return a not found error
  if (Math.random() < 0.5) {
    throw notFound()
  }

  // Or return some stuff
  return {
    stuff: 'stuff',
  }
})

export const Route = createFileRoute('/stuff')({
  loader: async () => {
    const stuff = await getStuff()

    return {
      stuff,
    }
  },
})

Not-Found-Fehler sind ein Kernmerkmal von TanStack Router.

Fehlerbehandlung

Wenn eine Server-Funktion einen Fehler (nicht-redirect/nicht-notFound) auslöst, wird dieser serialisiert und als JSON-Antwort mit einem 500er-Statuscode an den Client gesendet. Dies ist für das Debugging nützlich, aber Sie möchten diese Fehler möglicherweise benutzerfreundlicher behandeln. Dies können Sie tun, indem Sie den Fehler abfangen und in Ihrem Route-Lebenszyklus, Ihrer Komponente oder Ihrem Hook behandeln, wie Sie es normalerweise tun würden.

tsx
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  undefined.foo()
})

export const Route = createFileRoute('/stuff')({
  loader: async () => {
    try {
      await doStuff()
    } catch (error) {
      // Handle the error:
      // error === {
      //   message: "Cannot read property 'foo' of undefined",
      //   stack: "TypeError: Cannot read property 'foo' of undefined\n    at doStuff (file:///path/to/file.ts:3:3)"
    }
  },
})
import { createServerFn } from '@tanstack/solid-start'

export const doStuff = createServerFn({ method: 'GET' }).handler(async () => {
  undefined.foo()
})

export const Route = createFileRoute('/stuff')({
  loader: async () => {
    try {
      await doStuff()
    } catch (error) {
      // Handle the error:
      // error === {
      //   message: "Cannot read property 'foo' of undefined",
      //   stack: "TypeError: Cannot read property 'foo' of undefined\n    at doStuff (file:///path/to/file.ts:3:3)"
    }
  },
})

No-JS Server-Funktionen

Ohne aktivierte JavaScript gibt es nur eine Möglichkeit, Server-Funktionen auszuführen: durch das Absenden eines Formulars.

Dies geschieht durch Hinzufügen eines form-Elements zur Seite mit dem HTML-Attribut action.

Beachten Sie, dass wir das **HTML**-Attribut action erwähnt haben. Dieses Attribut akzeptiert in HTML nur einen String, wie alle anderen Attribute auch.

Zur Veranschaulichung: Während React 19 die Unterstützung für die Übergabe einer Funktion an action hinzugefügt hat, ist dies eine React-spezifische Funktion und kein Teil des HTML-Standards.

Das Attribut action gibt dem Browser an, wohin die Formulardaten gesendet werden sollen, wenn das Formular abgeschickt wird. In diesem Fall möchten wir die Formulardaten an die Server-Funktion senden.

Um dies zu tun, können wir die Eigenschaft url der Server-Funktion verwenden.

ts
const yourFn = createServerFn({ method: 'POST' })
  .validator((formData) => {
    if (!(formData instanceof FormData)) {
      throw new Error('Invalid form data')
    }

    const name = formData.get('name')

    if (!name) {
      throw new Error('Name is required')
    }

    return name
  })
  .handler(async ({ data: name }) => {
    console.log(name) // 'John'
  })

console.info(yourFn.url)
const yourFn = createServerFn({ method: 'POST' })
  .validator((formData) => {
    if (!(formData instanceof FormData)) {
      throw new Error('Invalid form data')
    }

    const name = formData.get('name')

    if (!name) {
      throw new Error('Name is required')
    }

    return name
  })
  .handler(async ({ data: name }) => {
    console.log(name) // 'John'
  })

console.info(yourFn.url)

Und dies dem action-Attribut des Formulars übergeben.

tsx
function Component() {
  return (
    <form action={yourFn.url} method="POST">
      <input name="name" defaultValue="John" />
      <button type="submit">Click me!</button>
    </form>
  )
}
function Component() {
  return (
    <form action={yourFn.url} method="POST">
      <input name="name" defaultValue="John" />
      <button type="submit">Click me!</button>
    </form>
  )
}

Wenn das Formular abgeschickt wird, wird die Server-Funktion ausgeführt.

No-JS Server-Funktionsargumente

Um Argumente an eine Server-Funktion beim Absenden eines Formulars zu übergeben, können Sie das input-Element mit dem Attribut name verwenden, um das Argument an das FormData anzuhängen, das an Ihre Server-Funktion übergeben wird.

tsx
const yourFn = createServerFn({ method: 'POST' })
  .validator((formData) => {
    if (!(formData instanceof FormData)) {
      throw new Error('Invalid form data')
    }

    const age = formData.get('age')

    if (!age) {
      throw new Error('age is required')
    }

    return age.toString()
  })
  .handler(async ({ data: formData }) => {
    // `age` will be '123'
    const age = formData.get('age')
    // ...
  })

function Component() {
  return (
    //  We need to tell the server that our data type is `multipart/form-data` by setting the `encType` attribute on the form.
    <form action={yourFn.url} method="POST" encType="multipart/form-data">
      <input name="age" defaultValue="34" />
      <button type="submit">Click me!</button>
    </form>
  )
}
const yourFn = createServerFn({ method: 'POST' })
  .validator((formData) => {
    if (!(formData instanceof FormData)) {
      throw new Error('Invalid form data')
    }

    const age = formData.get('age')

    if (!age) {
      throw new Error('age is required')
    }

    return age.toString()
  })
  .handler(async ({ data: formData }) => {
    // `age` will be '123'
    const age = formData.get('age')
    // ...
  })

function Component() {
  return (
    //  We need to tell the server that our data type is `multipart/form-data` by setting the `encType` attribute on the form.
    <form action={yourFn.url} method="POST" encType="multipart/form-data">
      <input name="age" defaultValue="34" />
      <button type="submit">Click me!</button>
    </form>
  )
}

Wenn das Formular abgeschickt wird, wird die Server-Funktion mit den Formulardaten als Argument ausgeführt.

No-JS Server-Funktionsrückgabewert

Unabhängig davon, ob JavaScript aktiviert ist oder nicht, gibt die Server-Funktion eine Antwort auf die vom Client ausgehende HTTP-Anfrage zurück.

Wenn JavaScript aktiviert ist, kann diese Antwort als Rückgabewert der Server-Funktion im JavaScript-Code des Clients abgerufen werden.

ts
const yourFn = createServerFn().handler(async () => {
  return 'Hello, world!'
})

// `.then` is not available when JavaScript is disabled
yourFn().then(console.log)
const yourFn = createServerFn().handler(async () => {
  return 'Hello, world!'
})

// `.then` is not available when JavaScript is disabled
yourFn().then(console.log)

Wenn jedoch JavaScript deaktiviert ist, gibt es keine Möglichkeit, den Rückgabewert der Server-Funktion im JavaScript-Code des Clients abzurufen.

Stattdessen kann die Server-Funktion eine Antwort an den Client liefern, die den Browser anweist, auf eine bestimmte Weise zu navigieren.

In Kombination mit einem loader von TanStack Router können wir eine Erfahrung ähnlich einer Single-Page-Application bieten, auch wenn JavaScript deaktiviert ist; alles, indem wir dem Browser anweisen, die aktuelle Seite mit neuen Daten neu zu laden, die durch den loader geleitet werden.

tsx
import * as fs from 'fs'
import { createFileRoute } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

const filePath = 'count.txt'

async function readCount() {
  return parseInt(
    await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
  )
}

const getCount = createServerFn({
  method: 'GET',
}).handler(() => {
  return readCount()
})

const updateCount = createServerFn({ method: 'POST' })
  .validator((formData) => {
    if (!(formData instanceof FormData)) {
      throw new Error('Invalid form data')
    }

    const addBy = formData.get('addBy')

    if (!addBy) {
      throw new Error('addBy is required')
    }

    return parseInt(addBy.toString())
  })
  .handler(async ({ data: addByAmount }) => {
    const count = await readCount()
    await fs.promises.writeFile(filePath, `${count + addByAmount}`)
    // Reload the page to trigger the loader again
    return new Response('ok', { status: 301, headers: { Location: '/' } })
  })

export const Route = createFileRoute('/')({
  component: Home,
  loader: async () => await getCount(),
})

function Home() {
  const state = Route.useLoaderData()

  return (
    <div>
      <form
        action={updateCount.url}
        method="POST"
        encType="multipart/form-data"
      >
        <input type="number" name="addBy" defaultValue="1" />
        <button type="submit">Add</button>
      </form>
      <pre>{state()}</pre>
    </div>
  )
}
import * as fs from 'fs'
import { createFileRoute } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'

const filePath = 'count.txt'

async function readCount() {
  return parseInt(
    await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
  )
}

const getCount = createServerFn({
  method: 'GET',
}).handler(() => {
  return readCount()
})

const updateCount = createServerFn({ method: 'POST' })
  .validator((formData) => {
    if (!(formData instanceof FormData)) {
      throw new Error('Invalid form data')
    }

    const addBy = formData.get('addBy')

    if (!addBy) {
      throw new Error('addBy is required')
    }

    return parseInt(addBy.toString())
  })
  .handler(async ({ data: addByAmount }) => {
    const count = await readCount()
    await fs.promises.writeFile(filePath, `${count + addByAmount}`)
    // Reload the page to trigger the loader again
    return new Response('ok', { status: 301, headers: { Location: '/' } })
  })

export const Route = createFileRoute('/')({
  component: Home,
  loader: async () => await getCount(),
})

function Home() {
  const state = Route.useLoaderData()

  return (
    <div>
      <form
        action={updateCount.url}
        method="POST"
        encType="multipart/form-data"
      >
        <input type="number" name="addBy" defaultValue="1" />
        <button type="submit">Add</button>
      </form>
      <pre>{state()}</pre>
    </div>
  )
}

Statische Server-Funktionen

Bei der Verwendung von Prerendering/Static-Generation können Server-Funktionen auch "statisch" sein, was es ermöglicht, ihre Ergebnisse zur Build-Zeit zu cachen und als statische Assets auszuliefern.

Erfahren Sie alles über dieses Muster auf der Seite Static Server Functions.

Wie werden Server-Funktionen kompiliert?

Unter der Haube werden Server-Funktionen aus dem Client-Bundle in ein separates Server-Bundle extrahiert. Auf dem Server werden sie wie sie sind ausgeführt, und das Ergebnis wird an den Client zurückgesendet. Auf dem Client leiten Server-Funktionen die Anfrage an den Server weiter, der die Funktion ausführt und das Ergebnis über fetch an den Client zurücksendet.

Der Prozess sieht folgendermaßen aus:

  • Wenn createServerFn in einer Datei gefunden wird, wird die innere Funktion auf eine use server-Direktive überprüft.
  • Wenn die use server-Direktive fehlt, wird sie am Anfang der Funktion hinzugefügt.
  • Auf dem Client wird die innere Funktion aus dem Client-Bundle in ein separates Server-Bundle extrahiert.
  • Die Client-seitige Server-Funktion wird durch eine Proxy-Funktion ersetzt, die eine Anfrage an den Server sendet, um die extrahierte Funktion auszuführen.
  • Auf dem Server wird die Server-Funktion nicht extrahiert und wie sie ist ausgeführt.
  • Nachdem die Extraktion stattgefunden hat, wendet jedes Bundle einen Prozess der Eliminierung von totem Code an, um ungenutzten Code aus jedem Bundle zu entfernen.
Unsere Partner
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
Prisma
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.