When working with Next.js, typing `getServerSideProps` correctly is a common question, especially if you want to maintain type safety and improve developer experience in a TypeScript codebase. Since `getServerSideProps` is a server-side function that fetches data at request time, understanding how to type it properly helps avoid runtime errors and makes your code easier to maintain.
I'll walk you through what `getServerSideProps` is, why typing it matters, how to type it correctly, and some practical tips and pitfalls I've encountered in real projects.
`getServerSideProps` is a Next.js data-fetching method that runs on the server on every request. It allows you to fetch data dynamically and pass it as props to your page component. Unlike `getStaticProps`, which runs at build time, `getServerSideProps` runs on each request, making it ideal for pages that need fresh data or depend on request-specific information like headers or cookies.
Here’s a quick example:
export async function getServerSideProps(context) {
const res = await fetch('https://api.example.com/data')
const data = await res.json()
return {
props: { data }, // will be passed to the page component as props
}
}
Typing `getServerSideProps` is more than just a nicety. It helps catch bugs early, documents the shape of your data, and improves IDE autocompletion. Without proper types, you risk runtime errors due to unexpected data shapes or missing properties. This is especially important when your data comes from external APIs or databases.
Typing also clarifies what your page component expects as props, making it easier for other developers (or your future self) to understand the contract between server-side data fetching and the UI.
Next.js provides built-in TypeScript types to help with this. The main type you want to use is GetServerSideProps from the next package.
Here’s a step-by-step approach:
First, define an interface or type for the props your page component will receive.
interface PageProps {
data: {
id: number
name: string
description: string
}
}
Import the type and annotate your `getServerSideProps` function:
import { GetServerSideProps } from 'next'
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
const res = await fetch('https://api.example.com/data')
const data = await res.json()
// You can add validation here to ensure data matches PageProps.data shape
return {
props: { data },
}
}
This tells TypeScript that the props returned from `getServerSideProps` will match the `PageProps` interface. If you return something else, TypeScript will warn you.
To keep things consistent, type your page component props as well:
import { NextPage } from 'next'
const Page: NextPage<PageProps> = ({ data }) => {
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
</div>
)
}
export default Page
Sometimes you need to access query parameters or cookies in `getServerSideProps`. The `context` parameter is typed as GetServerSidePropsContext, which gives you typed access to params, query, req, and res.
Example:
import { GetServerSideProps } from 'next'
interface PageProps {
userId: string
userData: {
name: string
email: string
}
}
export const getServerSideProps: GetServerSideProps<PageProps> = async (context) => {
const { userId } = context.query
if (typeof userId !== 'string') {
return {
notFound: true,
}
}
const res = await fetch(`https://api.example.com/users/${userId}`)
const userData = await res.json()
return {
props: {
userId,
userData,
},
}
}
Here, we check that userId is a string before using it, which is a common source of bugs if you forget to validate query parameters.
GetServerSideProps, which means TypeScript can’t check if the returned props match the expected shape.zod or io-ts for schema validation.context.query values can be string or string[] and not validating accordingly.props, redirect, or notFound. Returning anything else will cause runtime errors.Because `getServerSideProps` runs on every request, it can become a bottleneck if your data fetching is slow or inefficient. Typing doesn’t directly affect performance, but well-typed code encourages better practices:
Promise.all.Since `getServerSideProps` runs on the server, you can safely access secrets like API keys or databases without exposing them to the client. However, you still need to be careful:
| Method | When to Use | Typing Considerations | Performance | Use Case Example |
|---|---|---|---|---|
| getServerSideProps | Fetch data on every request; dynamic data based on request | Use GetServerSideProps<Props> for typing; context includes request info |
Slower, runs on each request | User dashboard with real-time data |
| getStaticProps | Fetch data at build time; static content | Use GetStaticProps<Props>; no request context |
Fast, served from CDN | Blog posts, marketing pages |
| API Routes | Serverless functions; custom endpoints | Type request and response objects manually or with helper libs | Depends on function execution time | Form submission, authentication |
GetServerSideProps and GetServerSidePropsContext.getStaticProps and API routes, focusing on use cases and performance.Typing `getServerSideProps` is straightforward once you understand the built-in Next.js types. The key is to define your props interface clearly, use GetServerSideProps<Props> to type your function, and validate any external or user-provided data. This approach reduces bugs, improves code clarity, and makes your Next.js apps more maintainable.
In production, I always combine TypeScript types with runtime validation for external data, especially when working with third-party APIs or user input. This combination helps catch issues early and prevents unexpected crashes.
Finally, remember that `getServerSideProps` impacts performance since it runs on every request, so use it judiciously and optimize your data fetching accordingly.