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 rohen HTTP-Anfragen, Formularübermittlungen, Benutzerauthentifizierung und vieles 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 ist, 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 beides 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 auch eine API-Route unter /users (wird aber mit einem Fehler abgebrochen, wenn doppelte Methoden definiert sind)
  • /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 zugeordnet haben. Wenn Sie also eine Datei namens routes/users.ts haben, die dem Anfragepfad von /users entspricht, können Sie keine anderen Dateien haben, die ebenfalls zur selben Route aufgelöst werden. Zum Beispiel würden die folgenden Dateien alle zur selben Route aufgelöst werden und zu einem Fehler führen:

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

Escaped Matching

Genau wie bei normalen Routen können Server-Routen auf escapte Zeichen abgestimmt werden. 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 Funktionalitäten rund um 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 sich aus übergeordneter Middleware "herauszubrechen"

Verschachtelte Verzeichnisse vs. Dateinamen

In den obigen Beispielen haben Sie vielleicht bemerkt, dass die Namenskonventionen für Dateien flexibel sind und es Ihnen ermöglichen, Verzeichnisse und Dateinamen zu mischen und anzupassen. 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 Starts createStartHandler in Ihrer server.ts-Einstiegsdatei verarbeitet.

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

export default createStartHandler({
  createRouter,
})(defaultStreamHandler)
// server.ts
import {
  createStartHandler,
  defaultStreamHandler,
} from '@tanstack/solid-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 Event an den Start-Handler übergeben

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

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

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

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

  return startHandler(event)
})

Definieren einer Server-Route

Server-Routen werden erstellt, indem ein ServerRoute aus einer Routendatei exportiert wird. Der ServerRoute-Export sollte durch Aufrufen 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)
  },
})

Definieren 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 Aufrufen der handler-Methode auf dem Methoden-Builder-Objekt für fortgeschrittenere Anwendungsfälle

Bereitstellen 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)
  },
})

Bereitstellen 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, der Methode Middleware 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 folgenden Eigenschaften:

  • request: Das eingehende Request-Objekt. Mehr über das Request-Objekt erfahren Sie in den MDN Web Docs.
  • params: Ein Objekt, das die dynamischen Pfadparameter der Route enthält. Wenn der Routenpfad beispielsweise /users/$id lautet und die Anfrage an /users/123 gerichtet ist, 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.

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

Dynamische Pfadparameter

Server-Routen unterstützen dynamische Pfadparameter auf die gleiche 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. Zum Beispiel 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. Zum Beispiel 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 Request-Objekt als erstes Argument, und Sie können auf den Request-Body ü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 Request-Body zugreifen.

Es ist wichtig zu beachten, dass die Methode request.json() ein Promise zurückgibt, das zu dem geparsten 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 Hilfsfunktion json

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

ts
// routes/hello.ts
import { json } from '@tanstack/solid-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/solid-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 festlegen, indem Sie entweder:

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

    ts
    // routes/hello.ts
    import { json } from '@tanstack/solid-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/solid-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/solid-start/server verwenden

    ts
    // routes/hello.ts
    import { json } from '@tanstack/solid-start'
    import { setResponseStatus } from '@tanstack/solid-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/solid-start'
    import { setResponseStatus } from '@tanstack/solid-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 festlegen.

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/solid-start/server verwenden.

    ts
    // routes/hello.ts
    import { setHeaders } from '@tanstack/solid-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/solid-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.