Build a Serverless Live Chat App with Next.js, Fauna & WunderGraph - WunderGraph
Prithwish Nath
January 21, 2023·19min read
Archive Notice
This article is archived and no longer maintained. It describes an earlier version of WunderGraph and a development workflow that is no longer part of the current product. The concepts, examples, and code may not work as described. For current documentation and guidance, see https://wundergraph.com/cosmo
A Step-by-Step Guide to creating a Scalable, Real-time Chat App using Serverless technologies…with a little help from NextAuth.js for GitHub sign-ins. Who needs WebSockets when you’ve got Live Queries? Not us!
State of GraphQL Federation 2026
How are teams governing schema changes, handling production traffic, and measuring Federation success? Share your experience and get early access to the full report.
If you’re building apps that work with realtime data, you’re probably using WebSockets. These allow a web browser and a web server to communicate in real-time by keeping a persistent connection open between the two – data is pushed to clients as soon as it becomes available, rather than having the client constantly poll the server to check for new data.
But what if your app is serverless – running on infrastructure managed by a cloud provider like AWS or GCP?
To facilitate high elasticity and fault tolerance, these environments are designed to be stateless and ephemeral by their very nature, meaning there’s no guarantee that your code will be running on the same physical server from one request to the next – and thus no persistent connection between the client and the server.
So what’s the solution to building realtime apps on serverless architectures? Let’s find out! Let’s build this realtime Slack/Discord-esque live chat using Next.js as our JS framework, Fauna (using GraphQL) as our database, and WunderGraph as a backend-for-frontend that facilitates the link between the two. Our app will also use GitHub sign-ins, and we’ll use the famous NextAuth (Now Auth.js!) for our auth needs.
Before we begin, though, let’s talk about how exactly we plan to solve the realtime data problem if we can’t use WebSockets.
Live Queries on the Server - The Secret Sauce
The GraphQL specification defines Subscriptions – you set up a stateful WebSocket connection between the client and server, and then subscribe to an event on the server. When the server sees an event that matches a defined Subscription, it sends requested data over the WebSocket connection to the client – et voila, we have our data.
💡 This explanation is a little handwave-y, but bear with me.
When you can’t use WebSockets (on serverless platforms, as mentioned before), you can’t use Subscriptions…but that’s where Live Queries come in.
They aren’t part of the GraphQL spec, so exact definitions differ by client library, but essentially, unlike Subscriptions, Live Queries aim to subscribe to the current state of server data – not events (new payloads whenever the query would yield different data)...and unlike Subscriptions, when WebSocket connections aren’t available, they can use plain old client-side HTTP polling at an interval, instead.
I can see readers picking up their pitchforks, right on cue.
Client-side polling for real-time data?! That’s insanely expensive, and what about handling multiple clients querying the same data?
And you’d be right! But…that’s where WunderGraph comes in. You see, we’re not going to be polling this data client-side. Instead, we’re pushing these Live Querying responsibilities onto WunderGraph, our Backend-for-Frontend, or API Gateway, whatever you want to call it.
WunderGraph is a dev tool that lets you define your data dependencies – GraphQL, REST APIs, Postgres and MySQL databases, Apollo Federations, and anything else you might think of – as config-as-code, and then it introspects them, turning all of it into a virtual graph that you can then query and mutate via GraphQL, and then expose as JSON-over-RPC for your client.
When you’re making Live Queries from the WunderGraph layer, no matter how many users are subscribed to your live chat on the frontend, you’ll only ever have a single polling instance active at any given time, for your entire app.
Much better than raw client-side polling; but is this perfect? No – the polling interval still adds latency and isn’t ideal for apps that need truly instant feedback, and without a JSON patch, the entire result of a query will be sent over the wire to the client every single time, even the unchanged data – but for our use-case, this is just fine.
With all of that out of the way, let’s get to the coding!
Architecture
First off, let’s talk about the flow of this app.
Our users sign in with their GitHub account (via OAuth) for the chatroom. We’re building a workgroup/internal team chat, and using GitHub as the provider for that makes sense.
Using NextAuth greatly streamlines our auth story – and it even has an official integration for Fauna – a serverless database! This makes the latter a really good pick as a database for both auth and business logic (storing chat messages).
Our Next.js frontend is relatively simple thanks to WunderGraph – it’ll provide us with auto-generated (and typesafe!) querying and mutation hooks built on top of Vercel’s SWR, and that’s what our components will use to fetch (chat messages and the list of online users) and write (new chat messages) data.
Let’s get started!
Step 0: The Setup
NextJS + WunderGraph
WunderGraph’s create-wundergraph-app CLI is the best way to set up both our BFF server and the Next.js frontend in one go, so let’s do just that. Just make sure you have the latest Node.js LTS installed, first.
Then, cd into the project directory, and
NextAuth
Our NextAuth setup involves a little busywork. Let’s get the base package out of the way first.
Next, get the NextAuth adapter for FaunaDB. An “adapter” in NextAuth.js connects your application to whatever database or backend system you want to use to store data for users, their accounts, sessions, etc. Adapters are technically optional, but since we want to persist user sessions for our app, we’ll need one.
Finally, we’ll need a “provider” – trusted services that can be used to sign in a user. You could define your own OAuth Provider if you wanted to, but here, we can just use the built-in GitHub provider.
Read the GitHub OAuth docs for how to register your app, configure it, and get the URL and Secret Key from your GitHub account (NOT optional). For the callback URL in your GitHub settings, use http://localhost:3000/api/auth/callback/github
Finally, once you have the GitHub client ID and secret key, put them in your Next.js ENV file (as GITHUB_ID and GITHUB_SECRET) respectively.
Step 1: The Data
Fauna is a geographically distributed (a perfect fit for the serverless/Edge era of Vercel and Netlify) document-relational database that aims to offer the best of both SQL (schema-based modeling) and NoSQL (flexibility, speed) worlds.
It offers GraphQL out of the box, but the most interesting thing about Fauna is that it makes it trivially easy to define stored procedures and expose them as GraphQL queries via the schema. (They call these User Defined Functions or UDFs). Very cool stuff! We’ll make use of this liberally.
- Sign up at Fauna
- Create a database (without sample data),
- Note down your URL and Secret in your Next.js ENV (as FAUNADB_GRAPHQL_URL and FAUNADB_TOKEN respectively) and move on to the next step.
Auth
To use Fauna as our Auth database, we’ll need to define its Collections (think tables) and Indexes (all searching in Fauna is done using these) in a certain way. NextAuth walks us through this procedure here, so it’s just a matter of copy-pasting these commands into your Fauna Shell.
Step 2 : Data Dependencies and Operations
WunderGraph works by introspecting all data sources you define in a dependency array and building data models for them.
First, make sure your ENV file is configured properly…
Replace with your own values, obviously. Also, double check to make sure the Fauna URL is set to the region you’re hosting your instance in!
…and then, add our Fauna database as a dependency in WunderGraph, and let it do its thing.
You can then write GraphQL queries/mutations to define operations on this data (these go in the .wundergraph/operations directory), and WunderGraph will generate typesafe Next.js client hooks for accessing them.
Step 3 : The Root
_app.tsx
You’ll have to wrap your app in
That’s All, Folks!
Bringing together NextAuth, Fauna, and WunderGraph for GraphQL Live Queries is a powerful combination that will serve you well in creating real-time, interactive chat experiences - whether you're building something simple for a small community or complex, enterprise-level platforms.
For Serverless applications, using WunderGraph as a backend-for-frontend with GraphQL Live Queries will give you far better latency than client-side polling - only one polling instance for all clients subscribed to the chat app equals reduced server load and network traffic.
Here are some things you should note, though, going forward:
For Quality of Life
Leave
liveQuery: truecommented out while you’re building the UI, and only turn it on while testing the chat features. You don’t want to thrash the Fauna server with calls – especially if on the limited free-tier!If you make changes to your Fauna GraphQL Schema during development, you’ll probably find that WunderGraph’s introspection doesn’t catch the new changes. This is intentional – it builds a cache after first introspection and works off that instead because you don’t want WunderGraph (which, in production, you’d deploy on Fly.io or WunderGraph Cloud) to waste resources doing full introspections every single time.
To get around this, run npx wunderctl generate –-clear-cache if you’ve made changes to your database schema during development and want the models and hooks regenerated.
For Deployments
- Deploying this combination is pretty easy; WunderGraph is a Go server and can be deployed to Fly.io or WunderGraph’s managed cloud platform, WunderGraph Cloud, Fauna has your data in managed instances anyway, and your Next.js frontend can be deployed to Vercel or Netlify.