Skip to content

Instantly share code, notes, and snippets.

@rogerchi
Created December 31, 2022 15:05
Show Gist options
  • Save rogerchi/26a922b53e86106fe462480ef3932abe to your computer and use it in GitHub Desktop.
Save rogerchi/26a922b53e86106fe462480ef3932abe to your computer and use it in GitHub Desktop.
Using DynamoDB for sessions in remix (File sessions in local dev)
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import { DeleteCommand, DynamoDBDocumentClient, GetCommand, PutCommand, UpdateCommand } from '@aws-sdk/lib-dynamodb'
import { Cookie, createCookie, createFileSessionStorage, createSessionStorage, SessionStorage } from '@remix-run/node'
import { v4 as uuidv4 } from 'uuid'
import { getEnv } from '~/utils/get-env'
const LOCAL_DEV = process.env.LOCAL_DEV === 'true'
if (LOCAL_DEV) {
// eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies, @typescript-eslint/no-var-requires
const dotenv = require('dotenv')
dotenv.config()
}
const client = new DynamoDBClient({})
const ddbDocClient = DynamoDBDocumentClient.from(client, {
marshallOptions: {
removeUndefinedValues: true,
},
})
function getTtl(now: Date) {
const utcMilllisecondsSinceEpoch = now.getTime() + now.getTimezoneOffset() * 60 * 1000
return Math.round(utcMilllisecondsSinceEpoch / 1000)
}
function createDynamoDbSessionStorage(sessionCookie: Cookie) {
return createSessionStorage({
cookie: sessionCookie,
async createData(data, expires) {
const id = uuidv4()
try {
await ddbDocClient.send(
new PutCommand({
TableName: getEnv('SESSIONS_TABLE'),
Item: {
pk: id,
session: data,
ttl: expires ? getTtl(expires) : undefined,
},
})
)
} catch (err) {
console.error(`PutCommand Error: ${err}`)
}
console.info(`Session created ID: ${id}`)
return id
},
async readData(id) {
console.info(`Session read ID: ${id}`)
let res
try {
res = await ddbDocClient.send(
new GetCommand({
TableName: getEnv('SESSIONS_TABLE'),
Key: {
pk: id,
},
})
)
} catch (err) {
console.error(`GetCommand Error: ${err}`)
}
return res?.Item?.session ?? null
},
async updateData(id, data, expires) {
try {
if (expires) {
await ddbDocClient.send(
new UpdateCommand({
TableName: getEnv('SESSIONS_TABLE'),
Key: {
pk: id,
},
UpdateExpression: 'set #session = :data, #ttl = :ttl',
ExpressionAttributeNames: {
'#session': 'session',
'#ttl': 'ttl',
},
ExpressionAttributeValues: {
':data': data,
':ttl': expires ? getTtl(expires) : undefined,
},
})
)
} else {
await ddbDocClient.send(
new UpdateCommand({
TableName: getEnv('SESSIONS_TABLE'),
Key: {
pk: id,
},
UpdateExpression: 'set #session = :data',
ExpressionAttributeNames: {
'#session': 'session',
},
ExpressionAttributeValues: {
':data': data,
},
})
)
}
} catch (err) {
console.error(`UpdateCommand Error: ${err}`)
}
},
async deleteData(id) {
try {
await ddbDocClient.send(
new DeleteCommand({
TableName: getEnv('SESSIONS_TABLE'),
Key: {
pk: id,
},
})
)
} catch (err) {
console.error(`DeleteCommand Error: ${err}`)
}
},
})
}
const SESSION_TIMEOUT = process.env.SESSION_TIMEOUT ? parseInt(process.env.SESSION_TIMEOUT) : 3600
const cookie = createCookie('__session', {
secrets: [getEnv('COOKIE_SECRET')],
maxAge: SESSION_TIMEOUT,
sameSite: 'strict',
httpOnly: true,
path: '/',
domain: getEnv('COOKIE_DOMAIN'),
secure: !LOCAL_DEV,
})
let cookieSessionStorage: SessionStorage
if (LOCAL_DEV) {
cookieSessionStorage = createFileSessionStorage({
cookie,
dir: '/tmp',
})
} else {
cookieSessionStorage = createDynamoDbSessionStorage(cookie)
}
export const { commitSession, destroySession, getSession } = cookieSessionStorage
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment