Run code once per node server startup #15341
-
We basically want to initialize few application/site level parameters on the server which can be reused (at server) on each request and updated on set interval. There is a way to do this when the node server starts but as we are using next.js built-in server, it doesn't seem straightforward.
Thank you |
Beta Was this translation helpful? Give feedback.
Replies: 49 comments 106 replies
-
Hey, did you find any solution for that? I am stuck with the same issue. |
Beta Was this translation helpful? Give feedback.
-
Same here. |
Beta Was this translation helpful? Give feedback.
-
Think you need to create a custom server. Fetch the settings in the prepare method and store in a json file and use the configs in getInitialProps. |
Beta Was this translation helpful? Give feedback.
-
The only idea I've got is to make a route which is going to be pinged by curl, but that sounds ridiculous. Custom server would really suck there. |
Beta Was this translation helpful? Give feedback.
-
I think that you can set environment key |
Beta Was this translation helpful? Give feedback.
-
I think this question is even more pertinent when we want to make a website that is pre-rendered using export functionality. There should be a way to do this easily. |
Beta Was this translation helpful? Give feedback.
-
We have started to use a singleton access pattern to get around this. The resource is not initialized until you call it for the first time. Example with import { Pool } from 'pg';
import getConfig from '../config';
let pool: Pool|null = null;
export async function query(incomingQuery, params = [], config:any = {}) {
if (!pool) {
console.log('🐘 Initializing Postgres connection!');
pool = new Pool({
connectionString: getConfig().databaseUrl,
max: getConfig().databaseClients ?? 10,
});
}
const results = await pool.query(incomingQuery, params);
return results;
}
export function getPool() {
return pool;
}
export async function disconnect() {
if (pool !== null) {
console.log('😵 Disconnecting from Postgres!');
return pool.end();
}
return new Promise(() => {});
} To use, simply run this at any place in your application: import { query, disconnect } from './postgres';
const result = await query('...');
console.log(result);
await disconnect(); |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Is there any limitation that prevents expose functions for startup and cleanup the server?
export default async function startup({ server }) {
/// Start-up logic, database connections, add middlewares to the server, etc...
console.log("Starting server...");
}
export default function cleanup() {
/// Clean-up logic
console.log("Shutting down server...");
} |
Beta Was this translation helpful? Give feedback.
-
Me too. |
Beta Was this translation helpful? Give feedback.
-
Did anyone found out how to initialize stuff? All the solutions around involve using a custom server or other hacky stuff... |
Beta Was this translation helpful? Give feedback.
-
It's a shame that we can't deal with low level stuff like initialisation / destruction in a low level abstraction of a http server |
Beta Was this translation helpful? Give feedback.
-
Not at the startup, but maybe at the first request as a workaround? It's now possible with middleware. Probably the same issue about destorying connections described in #15341 (reply in thread) exists in middleware, too. If you don't care about deinit, this could solve your problem.
import type { NextFetchEvent, NextRequest } from 'next/server';
const firstRequestedAt = new Date();
export function middleware(req: NextRequest, ev: NextFetchEvent) {
console.log(firstRequestedAt.toISOString());
} This outputs the same ISO string at every request.
However, once you move the logic to a different file to use module cache(which most of you are going for), it does not work as expected.
const firstRequestedAt = new Date();
const getFirstRequestedAt = () => firstRequestedAt;
export { getFirstRequestedAt };
import type { NextFetchEvent, NextRequest } from 'next/server';
import { getFirstRequestedAt } from 'path to util.ts';
export function middleware(req: NextRequest, ev: NextFetchEvent) {
console.log(getFirstRequestedAt().toISOString(), 'middleware');
}
import { getFirstRequestedAt } from 'path to util.ts';
export async function getStaticProps(context) {
console.log(getFirstRequestedAt().toISOString(), 'getStaticProps');
return {
props: {},
revalidate: 1,
};
}; This outputs a different ISO string for
Correct me if I was wrong, but this must be because import type { NextFetchEvent, NextRequest } from 'next/server';
// This gets executed at every middleware initialization, not every request but quite often.
const firstRequestedAt = new Date();
export function middleware(req: NextRequest, ev: NextFetchEvent) {
console.log(firstRequestedAt.toISOString());
} |
Beta Was this translation helpful? Give feedback.
-
A specific use-case for this is tracing with open telemetry. this must be initialized before anything else. A full custom-server to support this is overkill, and comes with many downsides. A workaround is to use
Even allowing for this, As others have noted, it would be far, far simpler to have a |
Beta Was this translation helpful? Give feedback.
-
I believe...there is now a solution? Last week, version 13.3.2 made changes for: Looking at the history, version 13.2.0 added the initial support: Checking out the docs from last week:
I haven't tried it myself yet, but this may be what we were looking for. |
Beta Was this translation helpful? Give feedback.
-
what about simply adding |
Beta Was this translation helpful? Give feedback.
-
We have a solution for this now! // instrumentation.ts
import { init } from 'package-init'
export function register() {
init()
} Let me know if this solves your use case. Related: #45628 |
Beta Was this translation helpful? Give feedback.
-
Any idea if it'd be ok to run operations on an interval from For example:
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Hey everybody. Looks like |
Beta Was this translation helpful? Give feedback.
-
instrumentation.ts is run only once on server startup as part of server
initialization as far as I’m aware.
…On Tue, Feb 27, 2024 at 10:10 AM Tristan Rechenberger < ***@***.***> wrote:
Hey everybody. Looks like instrumentation.ts is not called when running
server actions. 😭 Is this intentional or a bug?
—
Reply to this email directly, view it on GitHub
<#15341 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEIG6K5UNBYOXHHJGCTKSO3YVYHPFAVCNFSM4PCWE5G2U5DIOJSWCZC7NNSXTOKENFZWG5LTONUW63SDN5WW2ZLOOQ5TQNRQHAZDCMI>
.
You are receiving this because you commented.Message ID: <vercel/next.
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
We use instrumentation.ts successfully in our project, but with some limitations:
|
Beta Was this translation helpful? Give feedback.
-
Some libraries do not support dynamic imports, so |
Beta Was this translation helpful? Give feedback.
-
In case this is helpful for anyone, here's what I'm doing to call an endpoint to revalidate the cache on startup: // instrumentation.ts
let initialLoad = false;
export async function register() {
console.log('Registering instrumentation');
if (
process.env.NEXT_RUNTIME === 'nodejs' &&
typeof window === 'undefined' &&
!initialLoad
) {
initialLoad = true;
try {
console.log('Revalidating cache on startup');
await import('./revalidate-cache.js');
} catch (error) {
console.error('Error revalidating cache on startup:', error);
}
}
} |
Beta Was this translation helpful? Give feedback.
-
Hm i just put my starup code in
But your code might be calles more than once, so you should use a flag that it is already run |
Beta Was this translation helpful? Give feedback.
-
// instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { getMediaLibrary, libHolder } = await import(
'@/data/media/MediaLibrary'
)
libHolder.library = await getMediaLibrary()
console.log('instrumentation', libHolder)
}
} Importing from anywhere causes the code to run again. If I do it using Looks like the instrumentation and the server actions run in a different context and hence, I'm back to square one. Is it even possible in NextJS to do this? |
Beta Was this translation helpful? Give feedback.
We have a solution for this now!
instrumentation
Let me know if this solves your use case.
Related: #45628