Next.js Discord

Discord Forum

How do I create an object that will live for the lifetime of a request under the app router?

Unanswered
Icelandic Sheepdog posted this in #help-forum
Open in Discord
Icelandic SheepdogOP
So, I'm trying to create an object/class that would be a singleton service for the lifetime of a request on the server-side (then it should be destroyed) examples of this would be:

* Flash message Manager that would allow multiple server components to add their own flash messages and eventually render them at one of the layouts

* OR a backend client that would use the cookies or some parameter from the a user's session to access a different backend on behalf of the user (and be able to fetch data from multiple endpoints using a user-specific token)

I found this article https://prismic.io/blog/advanced-nextjs-server-context which describes a "server-context" this is the closest thing to what I wanted however the approach suggested there doesn't really work well with places where the cache is disabled (For example API Routes that export a POST endpoint or any server component that comes after a middleware that accesses cookies to make sure a user is authenticated)

any pointers are appreciated 🙂 --> a similar example to what I want to achieve from the go world would be request context

9 Replies

out of curiosity what is use case? in general garbage collector runs behind screens so the system gets unstable under heavy traffic situations so as you mentioned as Go does, stack memory is safer and robust.
especially in App Router where functions are completely asynchronously, a request can live longer like 30 seconds
Icelandic SheepdogOP
@tafutada777 I might be missing something about Nextjs's paradigm indeed, but to me (a backend engineer) it felt like the most natural implementation to have some request state, or some global state that is aware of the requests. In addition to the examples I gave in the original question. If u'd connect to a DB, u'd like to have either:
* a global connection pool
* a connection per request (less handy than the pool but just for the sake of this discussion let's assume this is a passable idea)

I think my problem is mostly that I can't:
1. find a way in the app directory documentation to put app-wide init code (to init services/singletons like the said connection pool)
2. find a way to provide values that are going to be the same for the lifetime of a request regardless of how many times they're called (for example a single array that will collect all flash messages from different places throughout the request time before writing them to the session at the end of the request, no matter how many times I call the constructor of that flashmessage manager I want to always get the same singleton for the lifetime of a request, but then it should be able to return a separate object for another call from a different user request happening at the same time or afterwards 🤷‍♂️ )
Im a backend dev who are familiar with Go Gin and Rust Actix servers. you should think it simple. in general there are two memory areas: heap and stack. basically you can put app scope objects such as db connection pool in the heap memory, then pass a ref of the obj to handlers as a argument like context. it is common way in Go and Rust.
in case of Next.js, namely Node.js, 'import' statement works magically. Imported objects become singleton so it can be imported from multiple files, ie. handler functions with out passing via arguments.

see my poc that uses prisma.
https://github.com/tfutada/zenn-prisma/blob/main/lib/prisma.ts

but there is a caveat. when hosted in Vercel platform, functions can be deployed to deferent instances (Runtimes): Edge Runtime and AWS Lambda. thus global variables are not shared(because in different instances)
Plus Lambda instances do not live long so global variables like connection pool do not work well.
import {prisma} from '@/lib/prisma';

export default async function Posts() {
    const rows = await prisma.user.findMany();
plus node.js is single thread, so u do not need mutex, which multi thread runtime Go and Rust need.
fyi, in case of Rust Actix web, globa obj need to be injected as function arg like this

#[post("/tokenize")]
async fn tokenize(analyzer: web::Data<Analyzer>, query: web::Json<types::Query>) -> Result<web::Json<TokenResponse>, actix_web::Error> {
Icelandic SheepdogOP
cool thanks, I think what I was missing is that imports share state, I'm aware that lambdas have a smaller runtime so can't keep app state but I'm not concerned since I'm not (yet) planning on deploying on vercel.

out of curuisity though, vercel guarantees that a single request is served end-to-finish with the same runtime no? (as in they don't do some kind of magic that'd serve dynamic parts of a request from multiple different runtimes(lambdas) right?)
sorry but my comment sounds a bit misleading. the same request runs on the same runtime. you cannot switch runtimes middle of process. you can specify a runtime only on the top level, route and page.
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config