Building your app

Data on every page

Some data belongs on every page, like the app name or the number of unread messages. You can share it once instead of returning it from every controller.

Share from a middleware

Middleware runs on every request, which makes it a good place for data every page needs:

// src/shared-data.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common'
import { requestState, type AnyRequest } from 'nestjs-mvc'

@Injectable()
export class SharedDataMiddleware implements NestMiddleware {
  use(req: AnyRequest, res: unknown, next: () => void) {
    requestState(req).shared.appName = 'Acme CRM'
    next()
  }
}
// src/app.module.ts
@Module({ imports: [MvcModule.forRoot({ vite: {} })] })
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(SharedDataMiddleware).forRoutes('{*path}')
  }
}

Every page now gets an appName prop.

Read it anywhere

Shared data ends up in the page props, so any component can read it with usePage(). Usually that's your layout:

import { usePage } from 'nestjs-mvc/react'

export function AppLayout({ children }: { children: React.ReactNode }) {
  const { props } = usePage<{ appName: string }>()

  return (
    <>
      <header>{props.appName}</header>
      <main>{children}</main>
    </>
  )
}
<!-- frontend/layouts/AppLayout.vue -->
<script setup lang="ts">
import { usePage } from 'nestjs-mvc/vue'

const page = usePage<{ appName: string }>()
</script>

<template>
  <header>{{ page.props.appName }}</header>
  <main>
    <slot />
  </main>
</template>

Data that needs the database

Pass a function instead of a value, and it only runs when a page actually renders:

@Injectable()
export class SharedDataMiddleware implements NestMiddleware {
  constructor(private readonly messages: MessagesService) {}

  use(req: AnyRequest, res: unknown, next: () => void) {
    requestState(req).shared.unread = () => this.messages.countUnread()
    next()
  }
}

This matters for the logged in user as well. Middleware runs before your guards, so req.user isn't set yet at that point, but a function runs later, after the guards are done.

The logged in user

For the user you can skip the middleware. Tell nestjs-mvc which fields to share and it adds auth.user to every page. Authentication has the details.

MvcModule.forRoot({
  vite: {},
  auth: { share: (user: User) => ({ id: user.id, name: user.name }) },
})

Share from a single request

Inside a handler, guard or interceptor you can use ViewService.share(), which adds data for the current request only:

@Get()
@View('Dashboard')
dashboard() {
  this.view.share('locale', 'en-GB')
  return { stats: this.stats.today() }
}
Previous
Redirects