I’m somewhat new to typescript in NextJS and still learning some of the ins and outs of the framework.
I was following a tutorial in JS and have been converting it to TS. Perhaps this could be to be blame? Not too sure.
My folder structure is quite simple. I just have added a /utils folder in the root of my project:
/pages
/public
/styles
/utils
The problem is that in the tutorial, a global key-value store is created/exported in /utils
as follows:
/* utils/users.js */
export const users = {}
I converted it to TS as follows:
/* utils/users.ts */
type user_i = {
address: string,
nonce: Number
}
export const users : {
[key:string] : user_i,
}={}
But as I call 2 unique api calls from /pages/api/auth.ts then /pages/api/verify.ts, the first stores a new value in users global object, then second reads it.
/* /pages/api/auth.ts */
export default function auth(req: NextApiRequest, res: NextApiResponse) {
...
...
console.log(users);
}
At the end of auth.ts, I run console.log(users) and I can see the 1 value is loaded into the ‘users’ variable. Then at the beginning of verify.ts, I run console.log(users) and it shows as empty.
/* /pages/api/verify.ts */
export default function verify(req: NextApiRequest, res: NextApiResponse) {
console.log("useres: " + JSON.stringify(users));
...
}
So my question is, how can I share the object between the api calls without creating an entire DB for a single variable. From the tutorial, it seems like it works in js but maybe my ts conversion is causing problems.
Any help is appreciated. Thanks
>Solution :
In Next.js, each API route is designed to run as a separate serverless function, which means they do not share memory. The behavior you are observing is expected because each API route is essentially a separate instance, and they don’t have access to each other’s memory. This behavior is not related to your TypeScript conversion; it is a fundamental aspect of how Next.js works with API routes.
To share data between API routes, you’ll need to store the data externally, such as in a database, cache, or another persistence layer. However, if you want to avoid using a database for this use case, you can try alternative methods like in-memory cache or even cookies.
Using cookies to share data between API routes:
Install the cookie and @types/cookie packages:
npm install cookie
npm install --save-dev @types/cookie
Update your /pages/api/auth.ts:
import { NextApiRequest, NextApiResponse } from 'next';
import { serialize } from 'cookie';
import { users } from '../../utils/users';
export default function auth(req: NextApiRequest, res: NextApiResponse) {
// ...
// Set the cookie
const userCookie = serialize('user', JSON.stringify(users), {
httpOnly: true,
secure: process.env.NODE_ENV !== 'development',
sameSite: 'strict',
maxAge: 3600,
path: '/',
});
res.setHeader('Set-Cookie', userCookie);
// ...
console.log(users);
}
Update your /pages/api/verify.ts:
import { NextApiRequest, NextApiResponse } from 'next';
import { parse } from 'cookie';
export default function verify(req: NextApiRequest, res: NextApiResponse) {
const cookies = req.headers.cookie ? parse(req.headers.cookie) : {};
const users = cookies.user ? JSON.parse(cookies.user) : {};
console.log("users: " + JSON.stringify(users));
// ...
}
Using Redis to share data between API routes:
npm install redis
Set up a Redis instance. You can use a local instance for development or a managed Redis service like Redis Labs or AWS ElastiCache.
Create a Redis connection utility in your /utils folder:
// utils/redis.ts
import Redis from 'redis';
import { promisify } from 'util';
const client = Redis.createClient({
host: process.env.REDIS_HOST,
port: Number(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD,
});
client.on('error', (error) => {
console.error(`Redis client error: ${error}`);
});
// Convert Redis client methods to promises for easier use with async/await
export const getAsync = promisify(client.get).bind(client);
export const setAsync = promisify(client.set).bind(client);
export default client;
Use the Redis utility in your API routes:
// pages/api/auth.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { setAsync } from '../../utils/redis';
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
// ...
await setAsync('some_key', JSON.stringify(some_data));
// ...
}
// pages/api/verify.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { getAsync } from '../../utils/redis';
export default async function verify(req: NextApiRequest, res: NextApiResponse) {
const data = JSON.parse(await getAsync('some_key'));
// ...
}
This example uses cookies to store the users object and share it between API routes. Please note that this method is not suitable for sensitive or large data since cookies have size limitations and can be easily manipulated by the client. For more robust and secure data sharing, consider using a database or a caching solution like Redis.