Formulare und Mutationen

Formulare ermöglichen Ihnen das Erstellen und Aktualisieren von Daten in Webanwendungen. Next.js bietet eine leistungsstarke Möglichkeit, Formularübermittlungen und Datenmutationen mit API-Routen zu handhaben.

Gut zu wissen:

  • Wir werden bald die schrittweise Einführung des App-Routers empfehlen und Server Actions für die Handhabung von Formularübermittlungen und Datenmutationen verwenden. Server Actions ermöglichen es Ihnen, asynchrone Serverfunktionen zu definieren, die direkt aus Ihren Komponenten aufgerufen werden können, ohne manuell eine API-Route erstellen zu müssen.
  • API-Routen legen keine CORS-Header fest, was bedeutet, dass sie standardmäßig nur same-origin sind.
  • Da API-Routen auf dem Server ausgeführt werden, können wir sensible Werte (wie API-Schlüssel) über Umgebungsvariablen verwenden, ohne sie dem Client preiszugeben. Dies ist entscheidend für die Sicherheit Ihrer Anwendung.

Beispiele

Nur-Server-Formular

Mit dem Pages-Router müssen Sie manuell API-Endpunkte erstellen, um Daten sicher auf dem Server zu mutieren.

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const data = req.body
  const id = await createItem(data)
  res.status(200).json({ id })
}
export default function handler(req, res) {
  const data = req.body
  const id = await createItem(data)
  res.status(200).json({ id })
}

Rufen Sie dann die API-Route vom Client mit einem Event-Handler auf:

import { FormEvent } from 'react'

export default function Page() {
  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()

    const formData = new FormData(event.currentTarget)
    const response = await fetch('/api/submit', {
      method: 'POST',
      body: formData,
    })

    // Antwort bei Bedarf handhaben
    const data = await response.json()
    // ...
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit">Submit</button>
    </form>
  )
}
export default function Page() {
  async function onSubmit(event) {
    event.preventDefault()

    const formData = new FormData(event.target)
    const response = await fetch('/api/submit', {
      method: 'POST',
      body: formData,
    })

    // Antwort bei Bedarf handhaben
    const data = await response.json()
    // ...
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit">Submit</button>
    </form>
  )
}

Formularvalidierung

Wir empfehlen die Verwendung von HTML-Validierung wie required und type="email" für grundlegende clientseitige Formularvalidierung.

Für erweiterte serverseitige Validierung können Sie eine Schema-Validierungsbibliothek wie zod verwenden, um die Formularfelder vor der Datenmutation zu validieren:

import type { NextApiRequest, NextApiResponse } from 'next'
import { z } from 'zod'

const schema = z.object({
  // ...
})

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const parsed = schema.parse(req.body)
  // ...
}
import { z } from 'zod'

const schema = z.object({
  // ...
})

export default async function handler(req, res) {
  const parsed = schema.parse(req.body)
  // ...
}

Fehlerbehandlung

Sie können React-State verwenden, um eine Fehlermeldung anzuzeigen, wenn eine Formularübermittlung fehlschlägt:

import React, { useState, FormEvent } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState<boolean>(false)
  const [error, setError] = useState<string | null>(null)

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setIsLoading(true)
    setError(null) // Vorherige Fehler bei einer neuen Anfrage löschen

    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })

      if (!response.ok) {
        throw new Error('Failed to submit the data. Please try again.')
      }

      // Antwort bei Bedarf handhaben
      const data = await response.json()
      // ...
    } catch (error) {
      // Fehlermeldung erfassen, um sie dem Benutzer anzuzeigen
      setError(error.message)
      console.error(error)
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <div>
      {error && <div style={{ color: 'red' }}>{error}</div>}
      <form onSubmit={onSubmit}>
        <input type="text" name="name" />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Laden...' : 'Submit'}
        </button>
      </form>
    </div>
  )
}
import React, { useState } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState(null)

  async function onSubmit(event) {
    event.preventDefault()
    setIsLoading(true)
    setError(null) // Vorherige Fehler bei einer neuen Anfrage löschen

    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })

      if (!response.ok) {
        throw new Error('Failed to submit the data. Please try again.')
      }

      // Antwort bei Bedarf handhaben
      const data = await response.json()
      // ...
    } catch (error) {
      // Fehlermeldung erfassen, um sie dem Benutzer anzuzeigen
      setError(error.message)
      console.error(error)
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <div>
      {error && <div style={{ color: 'red' }}>{error}</div>}
      <form onSubmit={onSubmit}>
        <input type="text" name="name" />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Laden...' : 'Submit'}
        </button>
      </form>
    </div>
  )
}

Anzeigen des Ladezustands

Sie können React-State verwenden, um einen Ladezustand anzuzeigen, wenn ein Formular auf dem Server übermittelt wird:

import React, { useState, FormEvent } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState<boolean>(false)

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setIsLoading(true) // Ladezustand bei Beginn der Anfrage auf true setzen

    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })

      // Antwort bei Bedarf handhaben
      const data = await response.json()
      // ...
    } catch (error) {
      // Fehler bei Bedarf handhaben
      console.error(error)
    } finally {
      setIsLoading(false) // Ladezustand bei Abschluss der Anfrage auf false setzen
    }
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Laden...' : 'Submit'}
      </button>
    </form>
  )
}
import React, { useState } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState(false)

  async function onSubmit(event) {
    event.preventDefault()
    setIsLoading(true) // Ladezustand bei Beginn der Anfrage auf true setzen

    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })

      // Antwort bei Bedarf handhaben
      const data = await response.json()
      // ...
    } catch (error) {
      // Fehler bei Bedarf handhaben
      console.error(error)
    } finally {
      setIsLoading(false) // Ladezustand bei Abschluss der Anfrage auf false setzen
    }
  }

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Laden...' : 'Submit'}
      </button>
    </form>
  )
}

Weiterleitung

Wenn Sie den Benutzer nach einer Mutation zu einer anderen Route weiterleiten möchten, können Sie mit redirect zu einer absoluten oder relativen URL weiterleiten:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const id = await addPost()
  res.redirect(307, `/post/${id}`)
}
export default async function handler(req, res) {
  const id = await addPost()
  res.redirect(307, `/post/${id}`)
}

Cookies setzen

Sie können Cookies in einer API-Route mit der setHeader-Methode der Antwort setzen:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.setHeader('Set-Cookie', 'username=lee; Path=/; HttpOnly')
  res.status(200).send('Cookie wurde gesetzt.')
}
export default async function handler(req, res) {
  res.setHeader('Set-Cookie', 'username=lee; Path=/; HttpOnly')
  res.status(200).send('Cookie wurde gesetzt.')
}

Cookies lesen

Sie können Cookies in einer API-Route mit dem cookies-Request-Helfer lesen:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const auth = req.cookies.authorization
  // ...
}
export default async function handler(req, res) {
  const auth = req.cookies.authorization
  // ...
}

Cookies löschen

Sie können Cookies in einer API-Route mit der setHeader-Methode der Antwort löschen:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.setHeader('Set-Cookie', 'username=; Path=/; HttpOnly; Max-Age=0')
  res.status(200).send('Cookie wurde gelöscht.')
}
export default async function handler(req, res) {
  res.setHeader('Set-Cookie', 'username=; Path=/; HttpOnly; Max-Age=0')
  res.status(200).send('Cookie wurde gelöscht.')
}