Server Actions and Mutations
Sunucu Eylemleri, sunucuda yürütülen zaman uyumsuz işlevlerdir. Next.js uygulamalarında form gönderimleri ve veri mutasyonlarını işlemek için Sunucu ve İstemci Bileşenlerinde kullanılabilirler.
🎥 İzleyin: Sunucu Eylemleri ile formlar ve mutasyonlar hakkında daha fazla bilgi edinin → YouTube (10 dakika).
Convention
Bir Sunucu Eylemi React ile tanımlanabilir
"use server"
yönergesini kullanabilirsiniz. Yönergeyi async
işlevinin en üstüne yerleştirerek işlevi bir Sunucu Eylemi olarak
işaretleyebilir ya da ayrı bir dosyanın en üstüne yerleştirerek bu dosyanın tüm dışa aktarımlarını Sunucu Eylemleri olarak
işaretleyebilirsiniz.
Server Components
Sunucu Bileşenleri satır içi işlev düzeyini veya modül düzeyinde "use server"
yönergesini kullanabilir. Bir Sunucu
Eylemini satır içi yapmak için, "use server"
adresini işlev gövdesinin en üstüne ekleyin:
// Server Component
export default function Page() {
// Server Action
async function create() {
'use server'
// ...
}
return (
// ...
)
}
Client Components
İstemci Bileşenleri yalnızca modül düzeyinde "use server"
yönergesini kullanan eylemleri içe aktarabilir.
Bir İstemci Bileşeninde bir Sunucu Eylemi çağırmak için yeni bir dosya oluşturun ve dosyanın başına
"use server"
yönergesini ekleyin. Dosya içindeki tüm fonksiyonlar, hem İstemci hem de Sunucu Bileşenlerinde
yeniden kullanılabilen Sunucu Eylemleri olarak işaretlenecektir:
'use server'
export async function create() {
// ...
}
import { create } from '@/app/actions'
export function Button() {
return (
// ...
)
}
Bir Sunucu Eylemini bir İstemci Bileşenine prop olarak da aktarabilirsiniz:
<ClientComponent updateItem={updateItem} />
'use client'
export default function ClientComponent({ updateItem }) {
return <form action={updateItem}>{/* ... */}</form>
}
Behavior
-
Sunucu eylemleri
<form>
öğesindekiaction
özniteliği kullanılarak çağrılabilir:- Sunucu Bileşenleri varsayılan olarak aşamalı geliştirmeyi destekler, yani JavaScript henüz yüklenmemiş veya devre dışı bırakılmış olsa da safra formu gönderilir.
- İstemci Bileşenlerinde, Sunucu Eylemlerini çağıran formlar, JavaScript henüz yüklenmemişse gönderimleri kuyruğa alır ve istemci hidrasyonuna öncelik verir.
- Hidrasyondan sonra, form gönderimi sırasında tarayıcı yenilenmez.
-
Sunucu Eylemleri
<form>
ile sınırlı değildir ve olay işleyicilerinden,useEffect
, üçüncü taraf kütüphanelerinden ve<button>
gibi diğer form öğelerinden çağrılabilir. - Sunucu Eylemleri Next.js önbelleğe alma ve yeniden doğrul ama mimarisiyle bütünleşir. Bir eylem çağrıldığında, Next.js hem güncellenmiş kullanıcı arayüzünü hem de yeni verileri tek bir sunucu gidiş gelişinde döndürebilir.
- Perde arkasında, eylemler
POST
yöntemini kullanır ve yalnızca bu HTTP yöntemi onları çağırabilir. - Sunucu Eylemlerinin argümanları ve dönüş değeri React tarafından serileştirilebilir olmalıdır. Serileştirilebilir argümanların ve değerlerin bir listesi için React dokümanlarına bakın .
- Sunucu Eylemleri fonksiyonlardır. Bu, uygulamanızın herhangi bir yerinde yeniden kullanılabilecekleri anlamına gelir.
- Sunucu Eylemleri, çalışma zamanını kullanıldıkları sayfa veya düzenden devralır.
Examples
Forms
React, HTML'i genişletir
<form>
Sunucu Eylemlerinin action
prop ile çağrılmasına izin vermek için öğe.
Bir formda çağrıldığında, eylem otomatik olarak
FormData
nesnesini kullanabilirsiniz. Alanları yönetmek için React useState
kullanmanıza gerek yoktur, bunun yerine yerel
FormData
yöntemlerini
kullanarak verileri ayıklayabilirsiniz
:
export default function Page() {
async function createInvoice(formData: FormData) {
'use server'
const rawFormData = {
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
}
// mutate data
// revalidate cache
}
return <form action={createInvoice}>...</form>
}
Bilmekte fayda var:
- Örnek: Yükleme ve Hata Durumlarına Sahip Form
- Çok sayıda alana sahip formlarla çalışırken
entries()
metodu ile JavaScript'inObject.fromEntries()
. Örneğin:const rawFormData = Object.fromEntries(formData.entries())
- Daha fazla bilgi edinmek için React
<form>
belgelerinebakın.
Passing Additional Arguments
JavaScript bind
yöntemini kullanarak bir Sunucu Eylemine ek bağımsız değişkenler iletebilirsiniz.
'use client'
import { updateUser } from './actions'
export function UserProfile({ userId }: { userId: string }) {
const updateUserWithId = updateUser.bind(null, userId)
return (
<form action={updateUserWithId}>
<input type="text" name="name" />
<button type="submit">Update User Name</button>
</form>
)
}
Sunucu Eylemi, form verilerine ek olarak userId
bağımsız değişkenini de alır:
'use server'
export async function updateUser(userId, formData) {
// ...
}
Bildiğim iyi oldu:
- Bir alternatif de argümanları formda gizli girdi alanları olarak iletmektir (örn.
<input type="hidden" name="userId" value={userId} />
). Ancak, değer işlenen HTML'nin bir parçası olacak ve kodlanmayacaktır..bind
hem Sunucu hem de İstemci Bileşenlerinde çalışır. Ayrıca aşamalı geliştirmeyi de destekler.
Pending states
React'i kullanabilirsiniz
useFormStatus
Form gönderilirken beklemede durumunu göstermek için kanca.
-
useFormStatus
belirli bir<form>
için durumu döndürür, bu nedenle<form>
öğesinin bir alt öğesi olarak tanımlanmalıdır. useFormStatus
bir React kancasıdır ve bu nedenle bir İstemci Bileşeninde kullanılmalıdır.
'use client'
import { useFormStatus } from 'react-dom'
export function SubmitButton() {
const { pending } = useFormStatus()
return (
<button type="submit" aria-disabled={pending}>
Add
</button>
)
}
<SubmitButton />
daha sonra herhangi bir biçimde iç içe geçebilir:
import { SubmitButton } from '@/app/submit-button'
import { createItem } from '@/app/actions'
// Server Component
export default async function Home() {
return (
<form action={createItem}>
<input type="text" name="field-name" />
<SubmitButton />
</form>
)
}
Server-side validation and error handling
Temel istemci tarafı form doğrulaması için required
ve type="email"
gibi HTML
doğrulamalarını kullanmanızı öneririz.
Daha gelişmiş sunucu tarafı doğrulama için, verileri değiştirmeden önce form alanlarını doğrulamak üzere zod gibi bir kütüphane kullanabilirsiniz:
'use server'
import { z } from 'zod'
const schema = z.object({
email: z.string({
invalid_type_error: 'Invalid Email',
}),
})
export default async function createUser(formData: FormData) {
const validatedFields = schema.safeParse({
email: formData.get('email'),
})
// Return early if the form data is invalid
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
}
}
// Mutate data
}
Alanlar sunucuda doğrulandıktan sonra, eyleminizde serileştirilebilir bir nesne döndürebilir ve React
useFormState
kullanıcıya bir mesaj göstermek için kanca.
-
Eylem
useFormState
adresine iletildiğinde, eylemin işlev imzası ilk bağımsız değişkeni olarak yeni birprevState
veyainitialState
parametresi alacak şekilde değişir. useFormState
bir React kancasıdır ve bu nedenle bir İstemci Bileşeninde kullanılmalıdır.
'use server'
export async function createUser(prevState: any, formData: FormData) {
// ...
return {
message: 'Please enter a valid email',
}
}
Ardından, eyleminizi useFormState
kancasına aktarabilir ve bir hata mesajı görüntülemek için döndürülen
state
adresini kullanabilirsiniz.
'use client'
import { useFormState } from 'react-dom'
import { createUser } from '@/app/actions'
const initialState = {
message: '',
}
export function Signup() {
const [state, formAction] = useFormState(createUser, initialState)
return (
<form action={formAction}>
<label htmlFor="email">Email</label>
<input type="text" id="email" name="email" required />
{/* ... */}
<p aria-live="polite" className="sr-only">
{state?.message}
</p>
<button>Sign up</button>
</form>
)
}
Bilmekte fayda var:
- Verileri değiştirmeden önce, her zaman bir kullanıcının eylemi gerçekleştirme yetkisine sahip olduğundan emin olmalısınız. Kimlik Doğrulama ve Yetkilendirme bölümüne bakın.
Optimistic updates
React'i kullanabilirsiniz
useOptimistic
kancası, yanıtı beklemek yerine Sunucu Eylemi bitmeden önce kullanıcı arayüzünü iyimser bir şekilde güncellemek için kullanılır:
'use client'
import { useOptimistic } from 'react'
import { send } from './actions'
type Message = {
message: string
}
export function Thread({ messages }: { messages: Message[] }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic<Message[]>(
messages,
(state: Message[], newMessage: string) => [
...state,
{ message: newMessage },
]
)
return (
<div>
{optimisticMessages.map((m, k) => (
<div key={k}>{m.message}</div>
))}
<form
action={async (formData: FormData) => {
const message = formData.get('message')
addOptimisticMessage(message)
await send(message)
}}
>
<input type="text" name="message" />
<button type="submit">Send</button>
</form>
</div>
)
}
Nested elements
<button>
, <input type="submit">
ve
<input type="image">
gibi <form>
içinde yuvalanmış öğelerde bir Sunucu
Eylemi çağırabilirsiniz. Bu öğeler formAction
prop veya
olay işleyicilerini kabul eder.
Bu, bir form içinde birden fazla sunucu eylemi çağırmak istediğiniz durumlarda kullanışlıdır. Örneğin, bir gönderi taslağını yayınlamanın yanı
sıra kaydetmek için belirli bir <button>
öğesi oluşturabilirsiniz. Daha fazla bilgi için
React <form>
dokümanlarına
bakın.
Programmatic form submission
Form gönderimini tetiklemek için
requestSubmit()
yöntemini kullanabilirsiniz. Örneğin, kullanıcı ⌘
+ Enter
tuşlarına bastığında
onKeyDown
olayını dinleyebilirsiniz:
'use client'
export function Entry() {
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (
(e.ctrlKey || e.metaKey) &&
(e.key === 'Enter' || e.key === 'NumpadEnter')
) {
e.preventDefault()
e.currentTarget.form?.requestSubmit()
}
}
return (
<div>
<textarea name="entry" rows={20} required onKeyDown={handleKeyDown} />
</div>
)
}
Bu, Sunucu Eylemini çağıracak olan en yakın <form>
atasının gönderimini tetikleyecektir.
Non-form Elements
Sunucu Eylemlerini <form>
öğeleri içinde kullanmak yaygın olsa da, olay işleyicileri ve
useEffect
gibi kodunuzun diğer bölümlerinden de çağrılabilirler.
Event Handlers
Bir Sunucu Eylemini onClick
gibi olay işleyicilerinden çağırabilirsiniz. Örneğin, bir beğeni sayısını artırmak
için:
'use client'
import { incrementLike } from './actions'
import { useState } from 'react'
export default function LikeButton({ initialLikes }: { initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes)
return (
<>
<p>Total Likes: {likes}</p>
<button
onClick={async () => {
const updatedLikes = await incrementLike()
setLikes(updatedLikes)
}}
>
Like
</button>
</>
)
}
Kullanıcı deneyimini iyileştirmek için aşağıdaki gibi diğer React API'lerini kullanmanızı öneririz
useOptimistic
ve
useTransition
Sunucu Eylemi sunucuda yürütülmeyi bitirmeden önce kullanıcı arayüzünü güncellemek veya bekleyen bir durumu göstermek için.
Form öğelerine olay işleyicileri de ekleyebilirsiniz, örneğin bir form alanını kaydetmek için onChange
:
'use client'
import { publishPost, saveDraft } from './actions'
export default function EditPost() {
return (
<form action={publishPost}>
<textarea
name="content"
onChange={async (e) => {
await saveDraft(e.target.value)
}}
/>
<button type="submit">Publish</button>
</form>
)
}
Birden fazla olayın hızlı bir şekilde art arda tetiklenebileceği bu gibi durumlarda, gereksiz Sunucu Eylemi çağrılarını önlemek için debouncing yapılmasını öneririz.
useEffect
React'i kullanabilirsiniz
useEffect
Bileşen bağlandığında veya bir bağımlılık değiştiğinde bir Sunucu Eylemini çağırmak için kanca. Bu, global olaylara bağlı olan veya otomatik
olarak tetiklenmesi gereken mutasyonlar için kullanışlıdır. Örneğin, uygulama kısayolları için onKeyDown
, sonsuz
kaydırma için bir kesişim gözlemci kancası veya bileşen bir görünüm sayısını güncellemek için bağlandığında:
'use client'
import { incrementViews } from './actions'
import { useState, useEffect } from 'react'
export default function ViewCount({ initialViews }: { initialViews: number }) {
const [views, setViews] = useState(initialViews)
useEffect(() => {
const updateViews = async () => {
const updatedViews = await incrementViews()
setViews(updatedViews)
}
updateViews()
}, [])
return <p>Total Views: {views}</p>
}
Davranışları ve uyarıları
dikkate almayı unutmayın
of
useEffect
.
Error Handling
Bir hata atıldığında, en yakın hata yakalanmış olacaktır
error.js
veya istemcide
<Suspense>
sınırı. Kullanıcı arayüzünüz tarafından işlenecek hataları döndürmek için
try/catch
adresini kullanmanızı öneririz.
Örneğin, Sunucu Eyleminiz yeni bir öğe oluştururken oluşan hataları bir mesaj döndürerek işleyebilir:
'use server'
export async function createTodo(prevState: any, formData: FormData) {
try {
// Mutate data
} catch (e) {
throw new Error('Failed to create task')
}
}
Bilmekte fayda var:
- Hatayı fırlatmanın yanı sıra,
useFormState
tarafından işlenecek bir nesne de döndürebilirsiniz. Sunucu tarafı doğrulama ve hata işleme bölümüne bakın.
Revalidating data
Next.js Önbelleğini Sunucu Eylemlerinizin içinde şu şekilde
yeniden doğrulayabilirsiniz
revalidatePath
API:
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost() {
try {
// ...
} catch (error) {
// ...
}
revalidatePath('/posts')
}
Veya bir önbellek etiketi ile belirli bir veri getirmeyi geçersiz kılmak için
revalidateTag
:
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost() {
try {
// ...
} catch (error) {
// ...
}
revalidateTag('posts')
}
Redirecting
Bir Sunucu Eylemi tamamlandıktan sonra kullanıcıyı farklı bir rotaya yönlendirmek isterseniz
redirect
API.
redirect
adresinin try/catch
bloğunun dışında çağrılması gerekir:
'use server'
import { redirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export async function createPost(id: string) {
try {
// ...
} catch (error) {
// ...
}
revalidateTag('posts') // Update cached posts
redirect(`/post/${id}`) // Navigate to the new post page
}
Cookies
get
, set
ve delete
çerezlerini bir Sunucu Eylemi içinde
cookies
API:
'use server'
import { cookies } from 'next/headers'
export async function exampleAction() {
// Get cookie
const value = cookies().get('name')?.value
// Set cookie
cookies().set('name', 'Delba')
// Delete cookie
cookies().delete('name')
}
Sunucu Eylemleri'nden çerezleri silmek için ek örneklere bakın.
Security
Authentication and authorization
Sunucu Eylemlerine genel kullanıma yönelik API uç noktalarına davrandığınız gibi davranmalı ve kullanıcının eylemi gerçekleştirme yetkisine sahip olduğundan emin olmalısınız. Örneğin:
'use server'
import { auth } from './lib'
export function addItem() {
const { user } = auth()
if (!user) {
throw new Error('You must be signed in to perform this action')
}
// ...
}
Closures and encryption
Bir bileşenin içinde bir Sunucu Eylemi tanımlamak, eylemin dış işlevin kapsamına erişebildiği bir
kapanışı
oluşturur. Örneğin, publish
eyleminin publishVersion
değişkenine erişimi vardır:
export default function Page() {
const publishVersion = await getLatestVersion();
async function publish(formData: FormData) {
"use server";
if (publishVersion !== await getLatestVersion()) {
throw new Error('The version has changed since pressing publish');
}
...
}
return <button action={publish}>Publish</button>;
}
Kapanışlar, daha sonra eylem çağrıldığında kullanılabilmesi için işleme sırasında bir veri anlık görüntüsü (örn.
publishVersion
) yakalamanız gerektiğinde kullanışlıdır.
Ancak bunun gerçekleşmesi için, yakalanan değişkenler istemciye gönderilir ve eylem çağrıldığında sunucuya geri gönderilir. Hassas verilerin istemciye ifşa edilmesini önlemek için Next.js, kapalı değişkenleri otomatik olarak şifreler. Bir Next.js uygulaması her oluşturulduğunda her eylem için yeni bir özel anahtar oluşturulur. Bu, eylemlerin yalnızca belirli bir yapı için çağrılabileceği anlamına gelir.
Bilmekte fayda var: Hassas değerlerin istemcide açığa çıkmasını önlemek için yalnızca şifrelemeye güvenmenizi önermiyoruz. Bunun yerine, belirli verilerin istemciye gönderilmesini proaktif olarak önlemek için React taint API'lerini kullanmalısınız.
Overwriting encryption keys (advanced)
Next.js uygulamanızı birden fazla sunucuda kendi kendine barındırırken, her sunucu örneği farklı bir şifreleme anahtarına sahip olabilir ve bu da potansiyel tutarsızlıklara yol açabilir.
Bunu azaltmak için process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
ortam değişkenini kullanarak şifreleme
anahtarının üzerine yazabilirsiniz. Bu değişkenin belirtilmesi, şifreleme anahtarlarınızın derlemeler arasında kalıcı olmasını ve tüm sunucu
örneklerinin aynı anahtarı kullanmasını sağlar.
Bu, birden fazla dağıtımda tutarlı şifreleme davranışının uygulamanız için kritik olduğu gelişmiş bir kullanım durumudur. Anahtar rotasyonu ve imzalama gibi standart güvenlik uygulamalarını göz önünde bulundurmalısınız.
Bilmekte fayda var: Vercel'e dağıtılan Next.js uygulamaları bunu otomatik olarak halleder.
Allowed origins (advanced)
Sunucu Eylemleri bir <form>
öğesinde çağrılabildiğinden, bu onları
CSRF saldırılarına
açar.
Sahne arkasında, Sunucu Eylemleri POST
yöntemini kullanır ve yalnızca bu HTTP yönteminin onları çağırmasına izin
verilir. Bu, modern tarayıcılardaki CSRF güvenlik açıklarının çoğunu, özellikle de
SameSite çerezleri
varsayılan olduğunda önler.
Ek bir koruma olarak, Next.js'deki Sunucu Eylemleri ayrıca
Origin baş
lığını
(veya
X-Forwarded-Host
)
Host baş
lığıyla karşılaştırır. Bunlar eşleşmezse istek iptal edilir. Başka bir deyişle, Sunucu Eylemleri yalnızca onu barındıran sayfayla aynı ana
bilgisayarda çağrılabilir.
Ters proxy veya çok katmanlı arka uç mimarileri (sunucu API'sinin üretim etki alanından farklı olduğu) kullanan büyük uygulamalar için
yapılandırma seçeneğinin kullanılması önerilir
serverActions.allowedOrigins
seçeneği güvenli kökenlerin bir listesini belirtmek için. Bu seçenek bir dizi string kabul eder.
/** @type {import('next').NextConfig} */
module.exports = {
experimental: {
serverActions: {
allowedOrigins: ['my-proxy.com', '*.my-proxy.com'],
},
},
}
Güvenlik ve Sunucu Eylemleri hakkında daha fazla bilgi için .
Additional resources
Sunucu Eylemleri hakkında daha fazla bilgi için aşağıdaki React dokümanlarına göz atın: