DEV Community

Cover image for Stateful vs Stateless Systems
Taki
Taki

Posted on

Stateful vs Stateless Systems

The terms stateful and stateless describe whether a system or component maintains context (state) between interactions (such as API calls, sessions, or processes).


Image description
(by @geeksforgeeks)

πŸ”„ Stateful

A stateful system remembers previous interactions. It maintains data (state) across requests or sessions.

βœ… Characteristics:

  • Stores session info between requests.
  • Requires memory/persistence layer (e.g., session store, database, in-memory cache).
  • Ideal for long-lived workflows or real-time interactions (e.g., chat, streaming, games).

πŸ“¦ Examples:

  • Stateful API: A login session stored on the server (e.g., express-session with Redis).
  • WebSocket connection: Keeps user state alive during a conversation.
  • Database connection pools: Keep the connection state between requests.

⚠️ Downsides:

  • Harder to scale (e.g., sticky sessions or external session store needed).
  • Higher memory usage and complexity in distributed systems.

🌐 Stateless

A stateless system forgets each interaction. Every request is treated independently and must contain all the needed information.

βœ… Characteristics:

  • No stored session data on the server.
  • Easy to scale horizontally.
  • More fault-tolerant and simple in cloud-native architectures.

πŸ“¦ Examples:

  • RESTful APIs: Every request must be self-contained (e.g., auth token in headers).
  • JWT Authentication: All session info is stored client-side in the token.
  • Serverless functions (e.g., AWS Lambda): Cold start = no memory of past calls.

⚠️ Trade-offs:

  • More data passed per request (e.g., token, context).
  • Harder to implement certain types of interactions (e.g., chat history, multi-step workflows).

🧠 Analogy

Imagine a stateless waiter in a restaurant. Every time you want something, you must reintroduce yourself and re-explain your order.

A stateful waiter remembers your table, your name, and your drink order β€” they’re context-aware.


πŸ”§ In a Fullstack GenAI App

Component Stateless or Stateful Why?
REST API (NestJS) Stateless Easier to scale, authenticate using JWT.
WebSocket Chat Stateful Needs to keep track of user sessions, conversation threads.
AI Context Memory Hybrid (stateless requests, stateful memory) You might store conversation history in Redis or a DB for context replay.
Frontend (Next.js) Mostly Stateless (per request) But stateful per session in the browser (via React state, cookies, localStorage).

Example

stateful vs stateless authentication with NESTJS

πŸ” 1. Stateless Authentication (JWT-based)

Best for: scalable, distributed systems (e.g., REST APIs, microservices).

🧠 Concept:

  • User logs in, receives a JWT token.
  • Token is sent with every request (usually via Authorization: Bearer <token> header).
  • Server doesn't store session – it just verifies the token.

βœ… Stateless Auth – NestJS Example

1. Auth Module Setup

// auth.module.ts
@Module({
  imports: [
    JwtModule.register({
      secret: process.env.JWT_SECRET,
      signOptions: { expiresIn: '1h' },
    }),
    PassportModule,
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}
Enter fullscreen mode Exit fullscreen mode

2. JWT Strategy

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_SECRET,
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Guard & Decorator

// jwt-auth.guard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

// usage in controller
@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {
  return req.user;
}
Enter fullscreen mode Exit fullscreen mode

🧩 2. Stateful Authentication (Session + Cookie-based)

Best for: monoliths, internal apps, or SSR apps where the backend and frontend share session context.

🧠 Concept:

  • User logs in β†’ server stores session in-memory or in Redis.
  • Client gets a session cookie (Set-Cookie).
  • Server uses the cookie to look up session data on each request.

βœ… Stateful Auth – NestJS Example (with express-session)

1. Install dependencies:

npm install express-session @nestjs/passport passport passport-local
Enter fullscreen mode Exit fullscreen mode

2. Main.ts Setup

// main.ts
import * as session from 'express-session';

app.use(
  session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: { secure: false }, // true if using HTTPS
  }),
);
Enter fullscreen mode Exit fullscreen mode

For distributed apps, add a Redis store:

import * as RedisStore from 'connect-redis';
import { createClient } from 'redis';

const redisClient = createClient();
await redisClient.connect();

app.use(
  session({
    store: new (RedisStore(session))({ client: redisClient }),
    ...
  }),
);
Enter fullscreen mode Exit fullscreen mode

3. Passport Session Setup

// auth.service.ts
async validateUser(username: string, password: string): Promise<any> {
  const user = await this.userService.findByUsername(username);
  if (user && user.password === password) return user;
  return null;
}
Enter fullscreen mode Exit fullscreen mode
// local.strategy.ts
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super();
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) throw new UnauthorizedException();
    return user;
  }
}
Enter fullscreen mode Exit fullscreen mode
// auth.controller.ts
@UseGuards(LocalAuthGuard)
@Post('login')
login(@Request() req) {
  // Session is now stored; req.session is available
  return req.user;
}

@Get('profile')
getProfile(@Request() req) {
  return req.user; // comes from session
}
Enter fullscreen mode Exit fullscreen mode

🧠 Summary Table

Feature Stateless (JWT) Stateful (Session + Cookie)
Server stores session? ❌ No βœ… Yes (Memory/Redis)
Scalability βœ… Great (easy scaling) ⚠️ Sticky sessions or shared store
Auth header required? βœ… Yes (Bearer token) ❌ No, browser handles cookies
Suited for APIs, SPAs SSR apps, internal tools
Token expiry? βœ… Client-controlled βœ… Server-controlled
Revocation (logout)? ❌ Hard (need token blacklist) βœ… Easy (delete session)

βš™οΈ When to Use What in NestJS GenAI App?

Use Case Recommendation
AI dictionary API (mobile or SPA) βœ… JWT (stateless)
Admin dashboard (SSR in Next.js) βœ… Session (stateful)
Real-time chat / conversation memory 🧠 Hybrid (stateful for chat memory, JWT for auth)

Top comments (2)

Collapse
 
nevodavid profile image
Nevo David

Pretty cool breakdown, honestly always makes me rethink how I set up auth every single time.

Collapse
 
tullis12 profile image
Tullis

Loved the restaurant analogy! It really makes the stateful vs stateless concept click. Super clear breakdownβ€”thanks!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.