You know React but feel stuck building to-do lists? Combining Next.js with Hasura gives you a full-stack, server-rendered app with a GraphQL API in under an hour.

We will build a simple dog shelter website with three routes: a homepage, a /dogs listing, and a detail view for each dog. The goal is a reusable SSR foundation you can scale into real projects.

1. Set up Next.js + TypeScript application

Boot the project from scratch:

npm init
npm install react react-dom next

The structure of a Next.js application revolves around the pages directory for defining routes.

2. Why Hasura for the backend?

Hasura gives you a full backend with a GraphQL API in minutes. You define your Postgres schema, and Hasura auto-generates queries, mutations, and subscriptions. No custom resolvers needed for basic CRUD.

  • Instant GraphQL API from any Postgres table
  • Built-in auth and permission rules
  • Real-time subscriptions out of the box

3. Connecting Next.js to Hasura with Apollo

Next.js shines when you leverage its server-side data fetching. Configure Apollo Client to point at your Hasura endpoint:

import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: `https://${process.env.NEXT_PUBLIC_API_HOST}`,
  cache: new InMemoryCache(),
});

The useQuery hook returns loading, error, and data for client-side fetching.

4. Fetch data on the server side

For SSR, use getInitialProps to fetch before the page renders:

Dogs.getInitialProps = async ({ apolloClient }) => {
  const response = await apolloClient.query({
    query: GET_DOGS,
  });

  return {
    dogs: response.data.dogs,
  };
};

This ensures the HTML arrives fully populated, which helps SEO and initial load time.

5. Deploy to Vercel

npm i -g vercel
vercel

That gives you a full-stack JamStack application with routes, TypeScript, CI/CD, GraphQL API, and SSR.

Practical Implementation: The USEO Approach

We have used this Next.js + Hasura stack for internal tools and client dashboards. One pattern we found critical is separating your GraphQL queries into a dedicated graphql/ directory with .ts files exporting typed query constants. This prevents query duplication and makes refactoring easier when Hasura schema changes.

For production apps, we replace getInitialProps with getServerSideProps (or getStaticProps where data changes infrequently). The difference matters: getInitialProps runs on both client and server, which can cause hydration mismatches if your data source behaves differently in each environment.

We also add a thin caching layer between Apollo and Hasura using Apollo’s fetchPolicy: 'cache-and-network'. This keeps the UI responsive on navigation while still fetching fresh data. For real-time features like notifications, Hasura subscriptions via WebSocket replace polling entirely, cutting server load significantly.