tsndr / cloudflare-worker-router

A super lightweight router (1.0K) with middleware support and ZERO dependencies for Cloudflare Workers.
MIT License
227 stars 30 forks source link

How to add resources to context? #34

Closed shirshak55 closed 8 months ago

shirshak55 commented 8 months ago

I want to add drizzle orm handler in request context.

import { RouterHandler, Router } from "@tsndr/cloudflare-worker-router"
import { NodePgDatabase } from "drizzle-orm/node-postgres"
import type * as schema from "../db/schema.js"
export type Var<T = string> = T
export type Secret<T = string> = T
export type Env = {}
export type ExtCtx = {
    db_client: NodePgDatabase<typeof schema>
} & ExecutionContext                     // --------------- Without this it will error in fetch
export type ExtReq = {
}
export type Handler = RouterHandler<Env, ExtCtx, ExtReq>

Here is my default fetch

export default {
    async fetch(request: Request, env: Env, ctx: ExtCtx) {
        let resp = applyHttpAuth(request)
        ctx.db_client = await db_client()
        if (resp === true) {
            return router.handle(request, env, ctx)
        } else {
            return resp
        }
    },
}

Now i am creating handler

router.get("/users", async (req) => {
        let users = await req.ctx!.db_client.query.users.findMany({})
        console.log({ users })
        return new Response(JSON.stringify(users))
    })

The problem is typescript says ctx might be undefined. I added ! (bang) for that. But it gives error that db_client cannot be found in ExecutionContext. It should be searching from ExtCtx instead of ExecutionContext?

Property 'db_client' does not exist on type 'ExecutionContext'.

Also if u don't mind whats the role of ExtReq?

Thank You Shirshak

tsndr commented 8 months ago

Hey, here's a little starter where I put sentry on the ctx:

import { Router, RouterHandler } from '@tsndr/cloudflare-worker-router'
import { Toucan } from 'toucan-js'
import { User } from './models/user.model'

import authMiddleware from './middlewares/auth.middleware'

import * as authController from './controllers/auth.controller'
import * as userController from './controllers/user.controller'

export type Var<T = string> = T
export type Secret<T = string> = T

export type Env = {
    ENV: Var<'dev' | 'prod'>
}

// Request Extension
export type ExtReq = {
    user?: User
}

// Context Extension
export type ExtCtx = {
    sentry?: Toucan
}

// Handler Type
export type Handler = RouterHandler<Env, ExtCtx, ExtReq>

// Initialize Router
const router = new Router<Env, ExtCtx, ExtReq>()

// Enable cors
router.cors()

// Auth
router.post('/auth/sign-up', authController.signUp)
router.post('/auth/sign-in', authController.signIn)
router.get('/auth/profile', authMiddleware, authController.profile)
router.post('/auth/profile', authMiddleware, authController.updateProfile)

export default {
    async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
        const sentry: Toucan = initSentry()
        try {
            return await router.handle(request, env, ctx, { sentry })
        } catch(err) {
            console.error(err)
            sentry?.captureException(err)
            return new Response(null, { status: 500 })
        }
    }
}

As seen in the example above, ExtReq can be used to extend req.

shirshak55 commented 8 months ago

@tsndr

Hi, thank you for the details.

Could u please tell me whats the difference between ext context and ext req.

Thank you.

tsndr commented 8 months ago
router.get('/user', ({ env, req, res, sentry }) => {
//                                    ^
//                                    This is where sentry would be available
    return Response.json({
        id: 1,
        name: 'John Doe'
    })
})
shirshak55 commented 8 months ago

thank you.