Skip to content

Instantly share code, notes, and snippets.

@jacob-ebey
Last active July 15, 2023 18:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jacob-ebey/d678283b0ae275bab23a1ec63d0a09fa to your computer and use it in GitHub Desktop.
Save jacob-ebey/d678283b0ae275bab23a1ec63d0a09fa to your computer and use it in GitHub Desktop.
Remix Upstash Session Storage
import { type Agent } from "https";
import {
createSessionStorage,
type RequestInit,
type SessionData,
type SessionIdStorageStrategy,
} from "@remix-run/node";
export interface UpstashSessionStorageOptions {
cookie?: SessionIdStorageStrategy["cookie"];
url: string;
token: string;
agent?: Agent;
generateId?: () => Promise<string> | string;
}
function defaultGenerateId() {
return fetch("https://uuid.rocks/plain").then((res) => {
if (!res.ok) {
throw new Error("Failed to generate a session id");
}
return res.text();
});
}
interface SessionContent {
data: SessionData;
expires?: string;
}
export function createUpstashSessionStorage({
cookie,
token,
url,
agent,
generateId = defaultGenerateId,
}: UpstashSessionStorageOptions) {
url = url.replace(/\/$/, "");
let upstashFetch = (path: string, options: RequestInit = {}) => {
options.headers = {
...options.headers,
Authorization: `Bearer ${token}`,
};
options.agent = agent;
return fetch(`${url}${path}`, options as globalThis.RequestInit);
};
return createSessionStorage({
cookie,
async createData(data, expires) {
let id = await generateId();
await upstashFetch(`/set/${id}`, {
method: "POST",
body: JSON.stringify({ data, expires }),
}).then(async (response) => {
if (!response.ok || (await response.json())?.result != "OK") {
throw new Error("Failed to create session");
}
});
return id;
},
deleteData(id) {
return upstashFetch(`/del/${id}`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
}).then(async (response) => {
if (!response.ok || !((await response.json())?.result > 0)) {
throw new Error("Failed to delete session");
}
});
},
async readData(id) {
const content = await upstashFetch(`/get/${id}`)
.then(async (response) => {
if (!response.ok) {
throw new Error("Failed to read session");
}
return response.json();
})
.then((json) => {
return JSON.parse(json.result) as SessionContent | null;
});
if (!content) {
return null;
}
const data = content.data;
const expires =
typeof content.expires === "string" ? new Date(content.expires) : null;
if (!expires || expires > new Date()) {
return data;
}
if (expires) {
await upstashFetch(`/del/${id}`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
});
}
return null;
},
updateData(id, data, expires) {
return upstashFetch(`/set/${id}`, {
method: "POST",
body: JSON.stringify({ data, expires }),
}).then(async (response) => {
if (!response.ok || (await response.json())?.result != "OK") {
throw new Error("Failed to update session");
}
});
},
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment