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: 32 suggested answers 29 replies
-
Hey, did you find any solution for that? I am stuck with the same issue. |
Beta Was this translation helpful? Give feedback.
-
Me too. |
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.
{{editor}}'s edit
{{editor}}'s edit
-
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.
{{editor}}'s edit
{{editor}}'s edit
-
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.
{{editor}}'s edit
{{editor}}'s edit
-
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 evert 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.
{{editor}}'s edit
{{editor}}'s edit
-
I hope this solution will solve your problem. This is an example of the startup function in next JS with not custom server This repository run startup function without using custom server. // before next loading
console.log(process.env.ENV_TEST, '[expected]: undefined') // undefined
// after next loading
const command = process.argv[2]
if (['dev', 'start'].includes(command)) {
require('./node_modules/next/dist/bin/next')
check(init)
}
// function
// function
// function
// next loading check
/** @param {Function} fn */
function check(fn) {
setTimeout(() => {
if (!('LOAD_CHECK' in process.env)) {
setTimeout(check, 0, fn)
return
}
fn()
}, 50)
}
// after init function
function init() {
switch (command) {
case 'dev': {
// here dev script
console.log('run dev')
break
}
case 'start': {
// here start script
console.log('run start')
break
}
}
// commons script
console.log(process.env.ENV_TEST, '[expected]: Hello!!') // 'Hello!!'
} For more information, see the app.js in this repository |
Beta Was this translation helpful? Give feedback.
{{editor}}'s edit
{{editor}}'s edit
-
I don't really know the Vercel team stance on this one, but I'll throw in my 2 cents. // next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
};
module.exports = (phase) => {
console.log("Starting at phase:", phase);
return nextConfig;
}; In this mode, AFAIK, this will run at exactly three points in time.
Here are some logs:
So technically, you could run some process in there at server startup, a sort of beacon to let others know you've attempted to launched the app. You do need to carry around the |
Beta Was this translation helpful? Give feedback.
-
As of next version 12.2.0 one way of solving this seems to be placing code int the I have a session service that needs to be initialized once per server startup. Init code int sessionService.ts export function initSessionService(kv: KeyValueProvider<Session>, t: TokenProvider) {
console.log("sessionService: try init");
if (!_service) {
_service = service(kv, t);
console.log("sessionService: initialized!", new Date().toISOString());
}
} pages/_app.ts import "../styles/globals.css";
import type { AppProps } from "next/app";
import memoryKeyValueProvider from "../backend/providers/memoryKeyValueProvider";
import tokenProvider from "../backend/providers/tokenProvider";
import { initSessionService } from "../backend/services/sessionService";
initSessionService(memoryKeyValueProvider(), tokenProvider());
function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default App; |
Beta Was this translation helpful? Give feedback.
-
Nextjs should have subscribeable events for this that accept promises:
The basic task of initialization is such a pain. You should not have to optin to a custom server to be able to do this. |
Beta Was this translation helpful? Give feedback.
-
Would like to bump this. Any resolution on this? |
Beta Was this translation helpful? Give feedback.
-
Yes I need this too to initialize Bull Queues |
Beta Was this translation helpful? Give feedback.
-
Discussion started at on 20 Jul 2020 and still going ... Is there any Issue created for this one as I could not find one :) |
Beta Was this translation helpful? Give feedback.
-
we need this option but the question is ( is vercel listening ) |
Beta Was this translation helpful? Give feedback.
-
I've seen no response in this discussion from a contributor. |
Beta Was this translation helpful? Give feedback.
{{editor}}'s edit
{{editor}}'s edit
-
Any update on this? I kinda regret choosing nextjs for my project. I should have informed myself better beforehand. But other than that I am very satisfied. |
Beta Was this translation helpful? Give feedback.
-
Did NextJS 13 solve this? I see there is something called "Server Components" now but not sure if this exact situation of running code once on the server is available on NextJS 13. |
Beta Was this translation helpful? Give feedback.
-
Oh dear... Just oh dear. So many awful hacks. Here's another to add to the list. I'm using typescript so the "node -r" and "next.config.js" techniques are not good enough for me. I ended up putting a call in Look for window object to detect client. It's pretty hacky and isn't great because your server side code gets bundled and sent to the client, even if it doesn't run. But it works if you need to do something like ensuring server side ENV vars exist properly at startup, rather than waiting for functionality to break because of missing ENV vars. |
Beta Was this translation helpful? Give feedback.
{{editor}}'s edit
{{editor}}'s edit
-
Would it possible for someone from the Next.js team to chime in? Two years is quite a while for a feature request to stay open, and 13 sounds like a great version number to add this in. Update: I've filed another issue, since this is a bit silly at this point. #43611 |
Beta Was this translation helpful? Give feedback.
-
Hey folks, wanted to mention this solution. I know it's not perfect, but it should unblock you from running code before the Next.js server starts without needing to eject out of the default server.
Source and credit: https://jake.tl/notes/2021-04-04-nextjs-preload-hack |
Beta Was this translation helpful? Give feedback.
-
Really important. |
Beta Was this translation helpful? Give feedback.
Hey folks, wanted to mention this solution. I know it's not perfect, but it should unblock you from running code before the Next.js server starts without needing to eject out of the default server.
Source and credit: https://jake.tl/notes/2021-04-04-nextjs-preload-hack