Skip to content

Commit dca8d50

Browse files
authored
Merge pull request #3 from wobsoriano/async-request-ctx
feat: createContext function
2 parents 1dffb4b + 2f1a6fa commit dca8d50

File tree

5 files changed

+78
-19
lines changed

5 files changed

+78
-19
lines changed

README.md

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,75 @@ Checkout [the playground example](/playground).
5959

6060
## H3 Event
6161

62-
The `useEvent` hook provides the `event` object of the current request:
62+
The `useEvent` hook provides the `event` object of the current request. You can use it to check headers, log requests, or extend the event's request object.
6363

6464
```ts
6565
import { useEvent } from 'nuxt-remote-fn/server'
66+
import { getRequestHeader, createError } from 'h3'
67+
import { decodeAndVerifyJwtToken } from '~/somewhere/in/utils'
6668

6769
export async function addTodo(todo: Todo) {
6870
const event = useEvent()
71+
72+
async function getUserFromHeader() {
73+
const authorization = getRequestHeader(event, 'authorization')
74+
if (authorization) {
75+
const user = await decodeAndVerifyJwtToken(authorization.split(' ')[1])
76+
return user
77+
}
78+
return null
79+
}
80+
81+
const user = await getUserFromHeader()
82+
83+
if (!user) {
84+
throw createError({ statusCode: 401 })
85+
}
86+
6987
const result = await prisma.todo.create({
7088
data: {
7189
...todo,
72-
userId: event.context.user.id
90+
userId: user.id
7391
}
7492
})
93+
7594
return result
7695
}
7796
```
7897

7998
You can use all built-in [h3 utilities](https://github.com/unjs/h3#built-in) inside your exported functions.
8099

100+
## createContext
101+
102+
Each `.server.` file can also export a `createContext` function that is called for each incoming request:
103+
104+
```ts
105+
export function createContext() {
106+
const event = useEvent()
107+
108+
async function getUserFromHeader() {
109+
const authorization = getRequestHeader(event, 'authorization')
110+
if (authorization) {
111+
const user = await decodeAndVerifyJwtToken(authorization.split(' ')[1])
112+
return user
113+
}
114+
return null
115+
}
116+
117+
event.context.user = await getUserFromHeader()
118+
}
119+
120+
export async function addTodo(todo: Todo) {
121+
const event = useEvent()
122+
123+
if (!event.context.user) {
124+
throw createError({ statusCode: 401 })
125+
}
126+
127+
// addTodo logic
128+
}
129+
```
130+
81131
## useAsyncData
82132

83133
`nuxt-remote-fn` can work seamlessly with [`useAsyncData`](https://nuxt.com/docs/api/composables/use-async-data/):

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@
5353
"fast-glob": "^3.2.12",
5454
"h3": "^1.0.2",
5555
"ofetch": "^1.0.0",
56-
"pathe": "^1.0.0",
57-
"unctx": "^2.1.1"
56+
"pathe": "^1.0.0"
5857
},
5958
"devDependencies": {
6059
"@nuxt/eslint-config": "^0.1.1",

playground/lib/todo.server.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ export function getTodo (id: number) {
1515
}
1616

1717
export async function toggleTodo (id: number) {
18-
const event = useEvent()
19-
console.log(event.context.params)
20-
2118
const todo = await getTodo(id)
2219
return prisma.todo.update({
2320
where: { id },
@@ -38,3 +35,8 @@ export function addTodo ({ title, content }: { title: string; content: string })
3835
}
3936
})
4037
}
38+
39+
export function createContext() {
40+
const event = useEvent()
41+
// console.log('event.context.params', event.context.params)
42+
}

pnpm-lock.yaml

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/runtime/server.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,49 @@
11
import { eventHandler, createError, readBody } from 'h3'
2-
import { createContext } from 'unctx'
2+
import { AsyncLocalStorage } from 'node:async_hooks';
33
import type { EventHandler, H3Event } from 'h3'
44

5-
const ctx = createContext<H3Event>()
5+
const DEFAULT_CONTEXT = {} as H3Event;
66

7-
export const useEvent = ctx.use
7+
const asyncLocalStorage = new AsyncLocalStorage<H3Event>();
8+
9+
export function useEvent(): H3Event {
10+
return asyncLocalStorage.getStore() || DEFAULT_CONTEXT;
11+
}
812

913
export function createRemoteFnHandler<
1014
F extends Record<string, Record<string, () => any>>,
1115
M extends keyof F,
1216
> (functions: F): EventHandler<any> {
13-
return eventHandler(async (event) => {
17+
const handler = eventHandler(async (event) => {
1418
const body = await readBody(event)
1519
const { moduleId, functionName } = event.context.params as {
1620
moduleId: M
1721
functionName: keyof F[M]
1822
}
19-
23+
2024
if (!(moduleId in functions)) {
2125
throw createError({
2226
statusCode: 400,
2327
statusMessage: `[nuxt-remote-fn]: Module ${moduleId as string} does not exist. Are you sure the file exists?`
2428
})
2529
}
26-
30+
2731
if (typeof functions[moduleId][functionName] !== 'function') {
2832
throw createError({
2933
statusCode: 400,
3034
statusMessage: `[nuxt-remote-fn]: ${functionName as string} is not a function.`
3135
})
3236
}
3337

34-
return ctx.call(event, () => {
35-
const result = functions[moduleId][functionName].apply(event, body.args)
36-
return result
37-
})
38+
if ('createContext' in functions[moduleId]) {
39+
await functions[moduleId]['createContext'].apply(event)
40+
}
41+
42+
const result = functions[moduleId][functionName].apply(event, body.args)
43+
return result
44+
})
45+
46+
return eventHandler((event) => {
47+
return asyncLocalStorage.run(event, () => handler(event))
3848
})
3949
}

0 commit comments

Comments
 (0)