shopify-app-template-node
shopify-app-template-node copied to clipboard
Example code for CustomSessionStorage of redis
Issue summary
I want example code which store session into redis.
I add the following code to this app. I refered to this page, but the code is ts.
// server.js
const sessionStorage = new RedisStore();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
// SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
sessionStorage.storeCallback,
sessionStorage.loadCallback,
sessionStorage.deleteCallback,
),
});
// redis-store.js
// Import the Session type from the library, along with the Node redis package, and `promisify` from Node
// import {Session} from '@shopify/shopify-api/dist/auth/session';
import redis from 'redis';
import {promisify} from 'util';
class RedisStore {
// private client: redis.RedisClient;
// private getAsync;
// private setAsync;
// private delAsync;
constructor() {
// Create a new redis client
this.client = redis.createClient();
// Use Node's `promisify` to have redis return a promise from the client methods
this.getAsync = promisify(this.client.get).bind(this.client);
this.setAsync = promisify(this.client.set).bind(this.client);
this.delAsync = promisify(this.client.del).bind(this.client);
}
/*
The storeCallback takes in the Session, and sets a stringified version of it on the redis store
This callback is used for BOTH saving new Sessions and updating existing Sessions.
If the session can be stored, return true
Otherwise, return false
*/
async storeCallback(session) {
try {
// Inside our try, we use the `setAsync` method to save our session.
// This method returns a boolean (true is successful, false if not)
console.log(this.setAsync)
return await this.setAsync(session.id, JSON.stringify(session))
} catch (err) {
// throw errors, and handle them gracefully in your application
throw new Error(err)
}
};
/*
The loadCallback takes in the id, and uses the getAsync method to access the session data
If a stored session exists, it's parsed and returned
Otherwise, return undefined
*/
async loadCallback(id) {
try {
// Inside our try, we use `getAsync` to access the method by id
// If we receive data back, we parse and return it
// If not, we return `undefined`
let reply = await this.getAsync(id);
if (reply) {
return JSON.parse(reply);
} else {
return undefined
}
} catch (err) {
throw new Error(err)
}
};
/*
The deleteCallback takes in the id, and uses the redis `del` method to delete it from the store
If the session can be deleted, return true
Otherwise, return false
*/
async deleteCallback (id) {
try {
// Inside our try, we use the `delAsync` method to delete our session.
// This method returns a boolean (true is successful, false if not)
return await this.delAsync(id)
} catch (err) {
throw new Error(err)
}
};
}
// Export the class
export default RedisStore;
My code causes following error.
Error: CustomSessionStorage failed to store a session. Error Details: Error: TypeError: this.setAsync is not a function
at SessionStorageError.ShopifyError [as constructor] (/home/nohara/work/orion-web/node_modules/@shopify/shopify-api/dist/error.js:13:28)
at new SessionStorageError (/home/nohara/work/orion-web/node_modules/@shopify/shopify-api/dist/error.js:172:42)
at CustomSessionStorage.<anonymous> (/home/nohara/work/orion-web/node_modules/@shopify/shopify-api/dist/auth/session/storage/custom.js:27:31)
at step (/home/nohara/work/orion-web/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:143:27)
at Object.throw (/home/nohara/work/orion-web/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:124:57)
at rejected (/home/nohara/work/orion-web/node_modules/@shopify/shopify-api/node_modules/tslib/tslib.js:115:69)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
You can find a good example here:
https://github.com/t-kelly/nextjs-shopify-app/blob/main/lib/redis.js https://github.com/t-kelly/nextjs-shopify-app/blob/main/lib/shopify.js#L12
Thanks for reporting this, we'll look into what might be going wrong here. In the meantime, Thomas' class is definitely a good alternative, thanks @dan-gamble!
@NoharaMasato I think you need to bind your methods:
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
sessionStorage.storeCallback.bind(sessionStorage),
sessionStorage.loadCallback.bind(sessionStorage),
sessionStorage.deleteCallback.bind(sessionStorage),
),
Or alternatively sack off OO as it's just creating confusion:
const createRedisStore = (config) => {
// Create a new redis client
const client = redis.createClient(config);
// Use Node's `promisify` to have redis return a promise from the client methods
const getAsync = promisify(this.client.get).bind(this.client);
const setAsync = promisify(this.client.set).bind(this.client);
const delAsync = promisify(this.client.del).bind(this.client);
const storeCallback = () => { ... }
const loadCallback = () => { ... }
const deleteCallback = () => { ... }
return { storeCallback, loadCallback, deleteCallback };
}
const redisStore = createRedisStore({});
// ...
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
redisStore.storeCallback,
redisStore.loadCallback,
redisStore.deleteCallback,
),
This issue is stale because it has been open for 60 days with no activity. It will be closed if no further action occurs in 14 days.
We are closing this issue because it has been inactive for a few months. This probably means that it is not reproducible or it has been fixed in a newer version. If it’s an enhancement and hasn’t been taken on since it was submitted, then it seems other issues have taken priority.
If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the CONTRIBUTING.md file for guidelines
Thank you!