Architect sessions
Developing database-backed, stateful web applications used to require a web server, a database server, a whole supporting cast of software and frameworks and all the near-constant maintenance those things required. Now anyone with a text editor can handle POST requests directly with a Lambda function and API Gateway. Read more about Functional Web Apps
The first primitive to understand for building stateful interactions on the web is session state. HTTP is a stateless protocol which is a fancy way of saying every HTTP request is like a completely clean slate. If we want to remember things between HTTP requests you need a session.
This guide will go over several ways to store session state within your app. There is an example app at the end that demonstrates how sessions work within Architect.
HTTP sessions
All @http
defined routes are session capable via @architect/functions
.
- Requests are tagged to a session via a stateless, signed, encrypted,
httpOnly
cookie:_idx
- Session data expires after a week of inactivity
This allows you to write fully stateful applications despite Lambda functions being completely stateless.
Manually read and write sessions:
// a simple request counter
let arc = require('@architect/functions')
exports.handler = async function http(req) {
// reads the session from the request
let session = await arc.http.session.read(req)
// modify the state
session.count = (session.count || 0) + 1
// write the session to a cookie
let cookie = await arc.http.session.write(session)
return {
statusCode: 200,
headers: {
'content-type': 'text/html; charset=utf8',
'set-cookie': cookie
},
body: `<pre>${JSON.stringify(session, null, 2)}</pre>`
}
}
Alternatively, use arc.http[.async]
's built-in session management for a simpler implementation:
let arc = require('@architect/functions')
async function handler(req) {
let session = req.session
session.count = (session.count || 0) + 1
return {
session,
html: `<pre>${JSON.stringify(session, null, 2)}</pre>`
}
}
exports.handler = arc.http.async(handler)
See the Node.js sessions reference for more details on
arc.http
andarc.http.session
.
Strong secret key
Ensure your app has a strong secret key. It should have a minimum length of 16 bytes.
npx arc --env production --add ARC_APP_SECRET something-much-better-than-this
🎲 Quickly generate a secret with openssl
:
npx arc env -e staging -a ARC_APP_SECRET $(openssl rand -hex 32)
npx arc env -e production -a ARC_APP_SECRET $(openssl rand -hex 32)
Example
1. Create an Arc project
mkdir -p ./mysesh
cd mysesh
npm init -f
npm install @architect/architect
2. Add app.arc
Create a project manifest app.arc
file at the root of your project.
# ./app.arc
@app
bigsesh
@http
get /
post /count
post /reset
And generate the boilerplate code by running:
npx arc init
3. Install Node.js helpers
Add the @architect/functions
runtime helper library to your project. This gives the request object a method to read and write sessions.
npm i @architect/functions
4. Read the session
Modify src/http/get-index/index.js
to read the session if it exists and render the forms with the session state
let arc = require('@architect/functions')
async function home(req) {
let count = req.session.count || 0
return {
// this is perfectly acceptable and FAST server side rendering
html: `
<!doctype html>
<html>
<body>
<form method=post action=/count>
<button>Count ${ count }</button>
</form>
<form method=post action=/reset>
<button>Reset</button>
</form>
</body>
</html>
`
}
}
exports.handler = arc.http.async(home)
5. Mutate the session
Modify src/http/post-count/index.js
to mutate the session and redirect home
let arc = require('@architect/functions')
async function counter(req) {
let count = (req.session.count || 0) + 1
return {
session: { count },
location: '/'
}
}
exports.handler = arc.http.async(counter)
FYI: Per recommended security practice Architect applications use
httpOnly
cookies for storing session state; anyone can implement their own mechanism using Set-Cookie headers directly
6. Clear the session
Modify src/http/post-reset/index.js
to clear the session state
let arc = require('@architect/functions')
async function reset(req) {
return {
session: {},
location: '/'
}
}
exports.handler = arc.http.async(reset)
For more information about
arc.http.async
helper, check out the documentation
7. Test locally
Preview by starting the dev server
npx arc sandbox
Navigate to localhost:3333
to test out the counter.