Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

run middleware after auth middleware #119

Merged
merged 9 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/example-todo-app/lib/middlewares/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./with-auth-token"
export * from "./with-route-spec"
export * from "./with-global-middeware-after-auth"
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Middleware } from "nextlove"

export const withGlobalMiddlewareAfterAuth: Middleware<{
auth: {
authorized_by: "auth_token"
seam: "withGlobalMiddlewareAfterAuth"
}
}> = (next) => async (req, res) => {
req.auth = {
...req.auth,
seam: "withGlobalMiddlewareAfterAuth",
}

return next(req, res)
}

export default withGlobalMiddlewareAfterAuth
8 changes: 7 additions & 1 deletion apps/example-todo-app/lib/middlewares/with-route-spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createWithRouteSpec, QueryArrayFormats } from "nextlove"
import { createWithRouteSpec, Middleware, QueryArrayFormats } from "nextlove"
import { withAuthToken } from "./with-auth-token"
export { checkRouteSpec } from "nextlove"
import * as ZT from "lib/zod"
import withGlobalMiddlewareAfterAuth from "./with-global-middeware-after-auth"

const defaultRouteSpec = {
authMiddlewareMap: { auth_token: withAuthToken },
Expand All @@ -17,6 +18,11 @@ const defaultRouteSpec = {

export const withRouteSpec = createWithRouteSpec(defaultRouteSpec)

export const withRouteSpecWithGlobalMiddlewareAfterAuth = createWithRouteSpec({
globalMiddlewaresAfterAuth: [withGlobalMiddlewareAfterAuth],
...defaultRouteSpec,
} as const)

export const withRouteSpecSupportedArrayFormats = (
supportedArrayFormats: QueryArrayFormats
) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
checkRouteSpec,
withRouteSpecWithGlobalMiddlewareAfterAuth,
} from "lib/middlewares"
import { z } from "zod"
import { v4 as uuidv4 } from "uuid"

export const jsonBody = z.object({
id: z.string().uuid().optional().default(uuidv4()),
title: z.string(),
completed: z.boolean().optional().default(false),
})

export const route_spec = checkRouteSpec({
methods: ["POST"],
auth: "auth_token",
jsonBody,
jsonResponse: z.object({
ok: z.boolean(),
auth: z.object({
authorized_by: z.literal("auth_token"),
seam: z.literal("withGlobalMiddlewareAfterAuth"),
}),
}),
})

export default withRouteSpecWithGlobalMiddlewareAfterAuth(route_spec)(
async (req, res) => {
return res.status(200).json({ ok: true, auth: req.auth })
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import test from "ava"
import getTestServer from "tests/fixtures/get-test-server"

test("POST /todo/add-with-global-middeware-after-auth", async (t) => {
const { axios } = await getTestServer(t)

axios.defaults.headers.common.Authorization = `Bearer auth_token`

const successfulRes = await axios
.post("/todo/add-with-global-middeware-after-auth", { title: "Todo Title" })
.catch((err) => err)
t.is(successfulRes.status, 200)
t.is(successfulRes.data.ok, true)
t.is(successfulRes.data.auth.authorized_by, "auth_token")
t.is(successfulRes.data.auth.seam, "withGlobalMiddlewareAfterAuth")
})
19 changes: 13 additions & 6 deletions packages/nextlove/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ export type QueryArrayFormats = readonly QueryArrayFormat[]

export interface SetupParams<
AuthMW extends AuthMiddlewares = AuthMiddlewares,
GlobalMW extends Middleware<any, any>[] = any[]
GlobalMW extends Middleware<any, any>[] = any[],
GlobalMWAfterAuth extends Middleware<any, any>[] = any[]
> {
authMiddlewareMap: AuthMW
globalMiddlewares: GlobalMW
globalMiddlewaresAfterAuth?: GlobalMWAfterAuth
exceptionHandlingMiddleware?: ((next: Function) => Function) | null

// These improve OpenAPI generation
Expand Down Expand Up @@ -110,17 +112,22 @@ type ErrorNextApiResponseMethods = {
json: Send<any>
}

export type RouteFunction<
SP extends SetupParams<AuthMiddlewares>,
RS extends RouteSpec
> = (
export type RouteFunction<SP extends SetupParams, RS extends RouteSpec> = (
req: (SP["authMiddlewareMap"] &
typeof defaultMiddlewareMap)[RS["auth"]] extends Middleware<
infer AuthMWOut,
any
>
? Omit<NextApiRequest, "query" | "body"> &
AuthMWOut &
MiddlewareChainOutput<
SP["globalMiddlewaresAfterAuth"] extends readonly Middleware<
any,
any
>[]
? SP["globalMiddlewaresAfterAuth"]
: []
> &
MiddlewareChainOutput<
RS["middlewares"] extends readonly Middleware<any, any>[]
? [...SP["globalMiddlewares"], ...RS["middlewares"]]
Expand Down Expand Up @@ -149,7 +156,7 @@ export type RouteFunction<
) => Promise<void>

export type CreateWithRouteSpecFunction = <
SP extends SetupParams<AuthMiddlewares, any>
SP extends SetupParams<AuthMiddlewares, any, any>
>(
setupParams: SP
) => <RS extends RouteSpec<string, any, any, any, any, any, z.ZodTypeAny, any>>(
Expand Down
2 changes: 2 additions & 0 deletions packages/nextlove/src/with-route-spec/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const createWithRouteSpec: CreateWithRouteSpecFunction = ((
const {
authMiddlewareMap = {},
globalMiddlewares = [],
globalMiddlewaresAfterAuth = [],
shouldValidateResponses,
shouldValidateGetRequestBody = true,
exceptionHandlingMiddleware = withExceptionHandling({
Expand Down Expand Up @@ -97,6 +98,7 @@ export const createWithRouteSpec: CreateWithRouteSpecFunction = ((
: []) as [any]),
...((globalMiddlewares || []) as []),
auth_middleware,
...((globalMiddlewaresAfterAuth || []) as []),
...((spec.middlewares || []) as []),
withMethods(spec.methods),
withValidation({
Expand Down
52 changes: 46 additions & 6 deletions packages/nextlove/tests/route-spec-types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { MiddlewareChainOutput } from "../src/types"
import {
checkRouteSpec,
createWithRouteSpec,
Middleware,
RouteSpec,
} from "../src"
import { checkRouteSpec, createWithRouteSpec, Middleware } from "../src"
import { expectTypeOf } from "expect-type"
import { z } from "zod"

Expand Down Expand Up @@ -227,3 +222,48 @@ export const myRoute9 = withRouteSpec(myRoute9Spec)(async (req, res) => {
const errorApiResponse400 = res.status(400)
expectTypeOf(errorApiResponse400.json).toMatchTypeOf<(body: any) => void>()
})

export const myRoute10 = createWithRouteSpec({
authMiddlewareMap: {},
globalMiddlewaresAfterAuth: [
null as any as Middleware<{
good: true
}>,
],
apiName: "",
globalMiddlewares: [],
productionServerUrl: "",
} as const)({
auth: "none",
methods: ["GET"],
} as const)(async (req, res) => {
expectTypeOf(req.good).toMatchTypeOf<true>()
})

export const myRoute11 = createWithRouteSpec({
authMiddlewareMap: {
// seam: (next) => (req, res) => {},
},
globalMiddlewaresAfterAuth: [
null as any as Middleware<{
auth: {
authorized_by: "auth_token"
seam: "withGlobalMiddlewareAfterAuth"
}
}>,
],
apiName: "",
globalMiddlewares: [
((next) => async (req, res) => {
next(req, res)
}) as any as Middleware<{
gmw: true
}>,
],
productionServerUrl: "",
} as const)({
auth: "none",
methods: ["GET"],
} as const)(async (req, res) => {
expectTypeOf(req.auth.seam).toMatchTypeOf<"withGlobalMiddlewareAfterAuth">()
})
Loading