Server-Routen

Server-Routen sind ein mächtiges Feature von TanStack Start, das es Ihnen ermöglicht, serverseitige Endpunkte in Ihrer Anwendung zu erstellen. Sie sind nützlich für die Verarbeitung von Roh-HTTP-Anfragen, Formularübermittlungen, Benutzerauthentifizierung und vielem mehr.

Server-Routen können in Ihrem ./src/routes-Verzeichnis Ihres Projekts definiert werden, direkt neben Ihren TanStack Router-Routen und werden automatisch vom TanStack Start-Server verarbeitet.

Hier sehen Sie, wie eine einfache Server-Route aussieht

ts
// routes/hello.ts

export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return new Response('Hello, World!')
  },
})
// routes/hello.ts

export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return new Response('Hello, World!')
  },
})

Server-Routen und App-Routen

Da Server-Routen im selben Verzeichnis wie Ihre App-Routen definiert werden können, können Sie sogar dieselbe Datei für beide verwenden!

tsx
// routes/hello.tsx

export const ServerRoute = createServerFileRoute().methods({
  POST: async ({ request }) => {
    const body = await request.json()
    return new Response(JSON.stringify({ message: `Hello, ${body.name}!` }))
  },
})

export const Route = createFileRoute('/hello')({
  component: HelloComponent,
})

function HelloComponent() {
  const [reply, setReply] = useState('')

  return (
    <div>
      <button
        onClick={() => {
          fetch('/hello', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({ name: 'Tanner' }),
          })
            .then((res) => res.json())
            .then((data) => setReply(data.message))
        }}
      >
        Say Hello
      </button>
    </div>
  )
}
// routes/hello.tsx

export const ServerRoute = createServerFileRoute().methods({
  POST: async ({ request }) => {
    const body = await request.json()
    return new Response(JSON.stringify({ message: `Hello, ${body.name}!` }))
  },
})

export const Route = createFileRoute('/hello')({
  component: HelloComponent,
})

function HelloComponent() {
  const [reply, setReply] = useState('')

  return (
    <div>
      <button
        onClick={() => {
          fetch('/hello', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({ name: 'Tanner' }),
          })
            .then((res) => res.json())
            .then((data) => setReply(data.message))
        }}
      >
        Say Hello
      </button>
    </div>
  )
}

Dateibasierte Routenkonventionen

Server-Routen in TanStack Start folgen denselben dateibasierten Routing-Konventionen wie TanStack Router. Das bedeutet, dass jede Datei in Ihrem routes-Verzeichnis mit einem ServerRoute-Export als API-Route behandelt wird. Hier sind einige Beispiele

  • /routes/users.ts erstellt eine API-Route unter /users
  • /routes/users.index.ts erstellt ebenfalls eine API-Route unter /users (führt aber einen Fehler aus, wenn doppelte Methoden definiert werden)
  • /routes/users/$id.ts erstellt eine API-Route unter /users/$id
  • /routes/users/$id/posts.ts erstellt eine API-Route unter /users/$id/posts
  • /routes/users.$id.posts.ts erstellt eine API-Route unter /users/$id/posts
  • /routes/api/file/$.ts erstellt eine API-Route unter /api/file/$
  • /routes/my-script[.]js.ts erstellt eine API-Route unter /my-script.js

Eindeutige Routenpfade

Jede Route kann nur eine einzige Handler-Datei zugewiesen bekommen. Wenn Sie also eine Datei namens routes/users.ts haben, die dem Anfragepfad /users entspricht, können Sie keine anderen Dateien haben, die ebenfalls zu derselben Route aufgelöst werden. Beispielsweise würden die folgenden Dateien alle zur selben Route aufgelöst und einen Fehler verursachen

  • /routes/users.index.ts
  • /routes/users.ts
  • /routes/users/index.ts

Maskiertes Matching

Genau wie bei normalen Routen können Server-Routen maskierte Zeichen abgleichen. Beispielsweise erstellt eine Datei namens routes/users[.]json.ts eine API-Route unter /users.json.

Pfadlose Layout-Routen und Break-out-Routen

Aufgrund des einheitlichen Routing-Systems werden pfadlose Layout-Routen und Break-out-Routen für ähnliche Funktionen im Zusammenhang mit Server-Routen-Middleware unterstützt.

  • Pfadlose Layout-Routen können verwendet werden, um einer Gruppe von Routen Middleware hinzuzufügen
  • Break-out-Routen können verwendet werden, um aus übergeordneter Middleware "auszubrechen"

Verschachtelte Verzeichnisse vs. Dateinamen

In den obigen Beispielen haben Sie vielleicht bemerkt, dass die Dateibenennungskonventionen flexibel sind und es Ihnen ermöglichen, Verzeichnisse und Dateinamen zu mischen. Dies ist beabsichtigt und ermöglicht es Ihnen, Ihre Server-Routen so zu organisieren, wie es für Ihre Anwendung sinnvoll ist. Mehr dazu erfahren Sie im TanStack Router File-based Routing Guide.

Verarbeitung von Server-Routen-Anfragen

Server-Routen-Anfragen werden von der createStartHandler-Funktion von Start in Ihrer server.ts-Einstiegsdatei behandelt.

tsx
// server.ts
import {
  createStartHandler,
  defaultStreamHandler,
} from '@tanstack/react-start/server'
import { createRouter } from './router'

export default createStartHandler({
  createRouter,
})(defaultStreamHandler)
// server.ts
import {
  createStartHandler,
  defaultStreamHandler,
} from '@tanstack/react-start/server'
import { createRouter } from './router'

export default createStartHandler({
  createRouter,
})(defaultStreamHandler)

Der Start-Handler ist dafür verantwortlich, eine eingehende Anfrage einer Server-Route zuzuordnen und die entsprechende Middleware und den Handler auszuführen.

Denken Sie daran, wenn Sie den Server-Handler anpassen müssen, können Sie dies tun, indem Sie einen benutzerdefinierten Handler erstellen und dann das Ereignis an den Start-Handler weitergeben

tsx
// server.ts
import { createStartHandler } from '@tanstack/react-start/server'

export default defineHandler((event) => {
  const startHandler = createStartHandler({
    createRouter,
  })(defaultStreamHandler)

  return startHandler(event)
})
// server.ts
import { createStartHandler } from '@tanstack/react-start/server'

export default defineHandler((event) => {
  const startHandler = createStartHandler({
    createRouter,
  })(defaultStreamHandler)

  return startHandler(event)
})

Definition einer Server-Route

Server-Routen werden erstellt, indem ein ServerRoute aus einer Routendatei exportiert wird. Der ServerRoute-Export sollte durch Aufruf der Funktion createServerFileRoute erstellt werden. Das resultierende Builder-Objekt kann dann verwendet werden, um

  • Middleware auf Routenebene hinzufügen
  • Handler für jede HTTP-Methode definieren
ts
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return new Response('Hello, World! from ' + request.url)
  },
})
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return new Response('Hello, World! from ' + request.url)
  },
})

Definition eines Server-Routen-Handlers

Es gibt zwei Möglichkeiten, einen Handler für eine Server-Route zu definieren.

  • Eine Handler-Funktion direkt für die Methode bereitstellen
  • Durch Aufruf der handler-Methode auf dem Methoden-Builder-Objekt für fortgeschrittenere Anwendungsfälle

Bereitstellung einer Handler-Funktion direkt für die Methode

Für einfache Anwendungsfälle können Sie eine Handler-Funktion direkt für die Methode bereitstellen.

ts
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return new Response('Hello, World! from ' + request.url)
  },
})
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return new Response('Hello, World! from ' + request.url)
  },
})

Bereitstellung einer Handler-Funktion über das Methoden-Builder-Objekt

Für komplexere Anwendungsfälle können Sie eine Handler-Funktion über das Methoden-Builder-Objekt bereitstellen. Dies ermöglicht es Ihnen, Middleware zur Methode hinzuzufügen.

tsx
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods((api) => ({
  GET: api.middleware([loggerMiddleware]).handler(async ({ request }) => {
    return new Response('Hello, World! from ' + request.url)
  }),
}))
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods((api) => ({
  GET: api.middleware([loggerMiddleware]).handler(async ({ request }) => {
    return new Response('Hello, World! from ' + request.url)
  }),
}))

Handler-Kontext

Jeder Handler für eine HTTP-Methode erhält ein Objekt mit den folgenden Eigenschaften

  • request: Das eingehende Anfrageobjekt. Sie können mehr über das Request-Objekt in den MDN Web Docs lesen.
  • params: Ein Objekt, das die dynamischen Pfadparameter der Route enthält. Wenn der Routenpfad beispielsweise /users/$id lautet und die Anfrage an /users/123 gestellt wird, dann ist params { id: '123' }. Wir werden dynamische Pfadparameter und Wildcard-Parameter später in dieser Anleitung behandeln.
  • context: Ein Objekt, das den Kontext der Anfrage enthält. Dies ist nützlich, um Daten zwischen Middleware weiterzugeben.

Sobald Sie die Anfrage verarbeitet haben, können Sie ein Response-Objekt oder Promise<Response> zurückgeben oder sogar eine der Hilfsfunktionen von @tanstack/react-start verwenden, um die Antwort zu manipulieren.

Dynamische Pfadparameter

Server-Routen unterstützen dynamische Pfadparameter auf dieselbe Weise wie TanStack Router. Beispielsweise erstellt eine Datei namens routes/users/$id.ts eine API-Route unter /users/$id, die einen dynamischen id-Parameter akzeptiert.

ts
// routes/users/$id.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ params }) => {
    const { id } = params
    return new Response(`User ID: ${id}`)
  },
})

// Visit /users/123 to see the response
// User ID: 123
// routes/users/$id.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ params }) => {
    const { id } = params
    return new Response(`User ID: ${id}`)
  },
})

// Visit /users/123 to see the response
// User ID: 123

Sie können auch mehrere dynamische Pfadparameter in einer einzigen Route haben. Beispielsweise erstellt eine Datei namens routes/users/$id/posts/$postId.ts eine API-Route unter /users/$id/posts/$postId, die zwei dynamische Parameter akzeptiert.

ts
// routes/users/$id/posts/$postId.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ params }) => {
    const { id, postId } = params
    return new Response(`User ID: ${id}, Post ID: ${postId}`)
  },
})

// Visit /users/123/posts/456 to see the response
// User ID: 123, Post ID: 456
// routes/users/$id/posts/$postId.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ params }) => {
    const { id, postId } = params
    return new Response(`User ID: ${id}, Post ID: ${postId}`)
  },
})

// Visit /users/123/posts/456 to see the response
// User ID: 123, Post ID: 456

Wildcard/Splat-Parameter

Server-Routen unterstützen auch Wildcard-Parameter am Ende des Pfades, die durch ein $ gefolgt von nichts gekennzeichnet sind. Beispielsweise erstellt eine Datei namens routes/file/$.ts eine API-Route unter /file/$, die einen Wildcard-Parameter akzeptiert.

ts
// routes/file/$.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ params }) => {
    const { _splat } = params
    return new Response(`File: ${_splat}`)
  },
})

// Visit /file/hello.txt to see the response
// File: hello.txt
// routes/file/$.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ params }) => {
    const { _splat } = params
    return new Response(`File: ${_splat}`)
  },
})

// Visit /file/hello.txt to see the response
// File: hello.txt

Anfragen mit einem Body verarbeiten

Um POST-Anfragen zu verarbeiten, können Sie einen POST-Handler zum Routenobjekt hinzufügen. Der Handler erhält das Anfrageobjekt als erstes Argument und Sie können auf den Anfragebody über die Methode request.json() zugreifen.

ts
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods({
  POST: async ({ request }) => {
    const body = await request.json()
    return new Response(`Hello, ${body.name}!`)
  },
})

// Send a POST request to /hello with a JSON body like { "name": "Tanner" }
// Hello, Tanner!
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods({
  POST: async ({ request }) => {
    const body = await request.json()
    return new Response(`Hello, ${body.name}!`)
  },
})

// Send a POST request to /hello with a JSON body like { "name": "Tanner" }
// Hello, Tanner!

Dies gilt auch für andere HTTP-Methoden wie PUT, PATCH und DELETE. Sie können Handler für diese Methoden im Routenobjekt hinzufügen und über die entsprechende Methode auf den Anfragebody zugreifen.

Es ist wichtig zu beachten, dass die Methode request.json() ein Promise zurückgibt, das zum analysierten JSON-Body der Anfrage aufgelöst wird. Sie müssen das Ergebnis awaiten, um auf den Body zuzugreifen.

Dies ist ein gängiges Muster für die Verarbeitung von POST-Anfragen in Server-Routen. Sie können auch andere Methoden wie request.text() oder request.formData() verwenden, um auf den Body der Anfrage zuzugreifen.

Mit JSON antworten

Beim Zurückgeben von JSON mit einem Response-Objekt ist dies ein gängiges Muster

ts
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return new Response(JSON.stringify({ message: 'Hello, World!' }), {
      headers: {
        'Content-Type': 'application/json',
      },
    })
  },
})

// Visit /hello to see the response
// {"message":"Hello, World!"}
// routes/hello.ts
export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return new Response(JSON.stringify({ message: 'Hello, World!' }), {
      headers: {
        'Content-Type': 'application/json',
      },
    })
  },
})

// Visit /hello to see the response
// {"message":"Hello, World!"}

Verwendung der json-Hilfsfunktion

Oder Sie können die json-Hilfsfunktion verwenden, um automatisch den Content-Type-Header auf application/json zu setzen und das JSON-Objekt für Sie zu serialisieren.

ts
// routes/hello.ts
import { json } from '@tanstack/react-start'

export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return json({ message: 'Hello, World!' })
  },
})

// Visit /hello to see the response
// {"message":"Hello, World!"}
// routes/hello.ts
import { json } from '@tanstack/react-start'

export const ServerRoute = createServerFileRoute().methods({
  GET: async ({ request }) => {
    return json({ message: 'Hello, World!' })
  },
})

// Visit /hello to see the response
// {"message":"Hello, World!"}

Antworten mit einem Statuscode

Sie können den Statuscode der Antwort setzen, indem Sie entweder

  • ihn als Eigenschaft des zweiten Arguments für den Response-Konstruktor übergeben

    ts
    // routes/hello.ts
    import { json } from '@tanstack/react-start'
    
    export const ServerRoute = createServerFileRoute().methods({
      GET: async ({ request, params }) => {
        const user = await findUser(params.id)
        if (!user) {
          return new Response('User not found', {
            status: 404,
          })
        }
        return json(user)
      },
    })
    
    // routes/hello.ts
    import { json } from '@tanstack/react-start'
    
    export const ServerRoute = createServerFileRoute().methods({
      GET: async ({ request, params }) => {
        const user = await findUser(params.id)
        if (!user) {
          return new Response('User not found', {
            status: 404,
          })
        }
        return json(user)
      },
    })
    
  • die Hilfsfunktion setResponseStatus von @tanstack/react-start/server verwenden

    ts
    // routes/hello.ts
    import { json } from '@tanstack/react-start'
    import { setResponseStatus } from '@tanstack/react-start/server'
    
    export const ServerRoute = createServerFileRoute().methods({
      GET: async ({ request, params }) => {
        const user = await findUser(params.id)
        if (!user) {
          setResponseStatus(404)
          return new Response('User not found')
        }
        return json(user)
      },
    })
    
    // routes/hello.ts
    import { json } from '@tanstack/react-start'
    import { setResponseStatus } from '@tanstack/react-start/server'
    
    export const ServerRoute = createServerFileRoute().methods({
      GET: async ({ request, params }) => {
        const user = await findUser(params.id)
        if (!user) {
          setResponseStatus(404)
          return new Response('User not found')
        }
        return json(user)
      },
    })
    

In diesem Beispiel geben wir einen Statuscode von 404 zurück, wenn der Benutzer nicht gefunden wird. Sie können jeden gültigen HTTP-Statuscode mit dieser Methode setzen.

Header in der Antwort setzen

Manchmal müssen Sie Header in der Antwort setzen. Sie können dies tun, indem Sie entweder

  • ein Objekt als zweites Argument für den Response-Konstruktor übergeben.

    ts
    // routes/hello.ts
    export const ServerRoute = createServerFileRoute().methods({
      GET: async ({ request }) => {
        return new Response('Hello, World!', {
          headers: {
            'Content-Type': 'text/plain',
          },
        })
      },
    })
    
    // Visit /hello to see the response
    // Hello, World!
    
    // routes/hello.ts
    export const ServerRoute = createServerFileRoute().methods({
      GET: async ({ request }) => {
        return new Response('Hello, World!', {
          headers: {
            'Content-Type': 'text/plain',
          },
        })
      },
    })
    
    // Visit /hello to see the response
    // Hello, World!
    
  • Oder die Hilfsfunktion setHeaders von @tanstack/react-start/server verwenden.

    ts
    // routes/hello.ts
    import { setHeaders } from '@tanstack/react-start/server'
    
    export const ServerRoute = createServerFileRoute().methods({
      GET: async ({ request }) => {
        setHeaders({
          'Content-Type': 'text/plain',
        })
        return new Response('Hello, World!')
      },
    })
    
    // routes/hello.ts
    import { setHeaders } from '@tanstack/react-start/server'
    
    export const ServerRoute = createServerFileRoute().methods({
      GET: async ({ request }) => {
        setHeaders({
          'Content-Type': 'text/plain',
        })
        return new Response('Hello, World!')
      },
    })
    
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.