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

Add formatError property to Blitz server- & rpchandler setup #4124

Merged
merged 13 commits into from
May 1, 2023
Merged
6 changes: 6 additions & 0 deletions .changeset/chatty-gifts-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@blitzjs/next": minor
"@blitzjs/rpc": minor
---

Add ability to format the error on the server before returning it to the client.
1 change: 1 addition & 0 deletions apps/toolkit-app/src/auth/components/SignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type SignupFormProps = {

export const SignupForm = (props: SignupFormProps) => {
const [signupMutation] = useMutation(signup)

return (
<div>
<h1>Create an Account</h1>
Expand Down
1 change: 0 additions & 1 deletion apps/toolkit-app/src/auth/mutations/signup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import db from "db"
import { SecurePassword } from "@blitzjs/auth/secure-password"
import { Role } from "types"

export default async function signup(input, ctx) {
Expand Down
8 changes: 5 additions & 3 deletions apps/toolkit-app/src/blitz-server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { BlitzCliConfig } from "blitz"
import { BlitzLogger } from "blitz"
import { setupBlitzServer } from "@blitzjs/next"
import { AuthServerPlugin, PrismaStorage } from "@blitzjs/auth"
import { AuthServerPlugin, PrismaStorage, simpleRolesIsAuthorized } from "@blitzjs/auth"
import db from "db"
import { simpleRolesIsAuthorized } from "@blitzjs/auth"
import { BlitzLogger } from "blitz"

export const cliConfig: BlitzCliConfig = {
customTemplates: "src/templates",
Expand All @@ -27,6 +26,9 @@ const { gSSP, gSP, api } = setupBlitzServer({
isAuthorized: simpleRolesIsAuthorized,
}),
],
formatError: (error) => {
return new Error("Formatted error" + error.message)
},
logger: BlitzLogger({}),
})

Expand Down
10 changes: 9 additions & 1 deletion apps/toolkit-app/src/pages/api/rpc/[[...blitz]].ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { rpcHandler } from "@blitzjs/rpc"
import { api } from "src/blitz-server"

export default api(rpcHandler({ onError: console.log }))
export default api(
rpcHandler({
onError: console.log,
formatError: (error) => {
error.message = `FormatError handler: ${error.message}`
return error
},
})
)
2 changes: 1 addition & 1 deletion apps/toolkit-app/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const UserInfo = () => {
<Link href={Routes.LoginPage()} className={styles.loginButton}>
<strong>Login</strong>
</Link>
<Link href="/api/auth/github/login" passHref>
<Link href="/api/auth/github/login" passHref legacyBehavior>
<a className="button small">
<strong>Sign in with GitHub</strong>
</a>
Expand Down
12 changes: 6 additions & 6 deletions integration-tests/auth-with-rpc/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {describe, it, expect, beforeAll, afterAll} from "vitest"
import {afterAll, beforeAll, describe, expect, it} from "vitest"
import {
killApp,
findPort,
runBlitzCommand,
blitzLaunchApp,
blitzBuild,
blitzLaunchApp,
blitzStart,
findPort,
killApp,
runBlitzCommand,
waitFor,
} from "../../utils/next-test-utils"
import webdriver from "../../utils/next-webdriver"
Expand All @@ -15,7 +15,7 @@ let appPort: number

const runTests = () => {
describe("Auth", () => {
/* TODO - Add a non flaky Integration Test for custom plugin
/* TODO - Add a non flaky Integration Test for custom plugin
describe("custom plugin", () => {
it("custom plugin - events", async () => {
const browser = await webdriver(appPort, "/custom-plugin")
Expand Down
1 change: 1 addition & 0 deletions integration-tests/next-13-app-dir/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
15 changes: 4 additions & 11 deletions integration-tests/next-13-app-dir/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import {describe, it, expect, beforeAll, afterAll} from "vitest"
import {afterAll, beforeAll, describe, expect, it} from "vitest"
import waitPort from "wait-port"
import {
killApp,
findPort,
runBlitzCommand,
blitzLaunchApp,
blitzBuild,
blitzStart,
} from "../../utils/next-test-utils"
import {blitzLaunchApp, findPort, killApp, runBlitzCommand} from "../../utils/next-test-utils"
import webdriver from "../../utils/next-webdriver"

import fetch from "node-fetch"
Expand Down Expand Up @@ -43,7 +36,7 @@ const runTests = (mode?: string) => {
const browser = await webdriver(appPort, "/authenticated-client")
let errorMsg = await browser.elementById(`error`).text()
expect(errorMsg).toMatch(/Error: You are not authenticated/)
if (browser) browser.close()
if (browser) await browser.close()
},
5000 * 60 * 2,
)
Expand All @@ -55,7 +48,7 @@ const runTests = (mode?: string) => {
const browser = await webdriver(appPort, "/authenticated-server")
let errorMsg = await browser.elementById(`error`).text()
expect(errorMsg).toMatch(/Error: You are not authenticated/)
if (browser) browser.close()
if (browser) await browser.close()
},
5000 * 60 * 2,
)
Expand Down
3 changes: 2 additions & 1 deletion integration-tests/next-13-app-dir/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
{
"name": "next"
}
]
],
"strictNullChecks": true
},
"exclude": ["node_modules"],
"baseUrl": "."
Expand Down
12 changes: 12 additions & 0 deletions integration-tests/rpc/app/queries/getCustomStatusCodeFailure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class ErrorWithStatusCode extends Error {
statusCode

constructor(message, statusCode) {
super(message)
this.statusCode = statusCode
}
}

export default async function getCustomStatusCodeFailure() {
throw new ErrorWithStatusCode("Error with custom status code for test", 418)
}
9 changes: 4 additions & 5 deletions integration-tests/rpc/test/index.test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import {describe, it, expect, beforeAll, afterAll} from "vitest"
import fs from "fs-extra"
import {afterAll, beforeAll, describe, expect, it} from "vitest"
import {join} from "path"
import {
killApp,
fetchViaHTTP,
findPort,
killApp,
launchApp,
fetchViaHTTP,
nextBuild,
nextStart,
nextExport,
nextStart,
} from "../../utils/next-test-utils"

// jest.setTimeout(1000 * 60 * 2)
Expand Down
43 changes: 24 additions & 19 deletions packages/blitz-next/src/index-server.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
import type {
NextConfig,
GetServerSideProps,
GetServerSidePropsResult,
GetStaticProps,
GetStaticPropsResult,
NextApiRequest,
NextApiResponse,
NextConfig,
} from "next"
import {
AddParameters,
AsyncFunc,
BlitzLogger,
BlitzServerPlugin,
Ctx as BlitzCtx,
FirstParam,
RequestMiddleware,
MiddlewareResponse,
BlitzLogger,
handleRequestWithMiddleware,
initializeLogger,
Simplify,
UnionToIntersection,
isRouteUrlObject,
MiddlewareResponse,
reduceBlitzServerPlugins,
RouteUrlObject,
startWatcher,
stopWatcher,
} from "blitz"
import {handleRequestWithMiddleware, startWatcher, stopWatcher} from "blitz"
import {installWebpackConfig, InstallWebpackConfigOptions, ResolverPathOptions} from "@blitzjs/rpc"
import {
DefaultOptions,
QueryClient,
getQueryKey,
getInfiniteQueryKey,
dehydrate,
getInfiniteQueryKey,
getQueryKey,
installWebpackConfig,
InstallWebpackConfigOptions,
QueryClient,
ResolverPathOptions,
} from "@blitzjs/rpc"
import {IncomingMessage, ServerResponse} from "http"
import {withSuperJsonProps} from "./superjson"
import {ParsedUrlQuery} from "querystring"
import {PreviewData} from "next/types"
import {resolveHref} from "next/dist/shared/lib/router/utils/resolve-href"
import {RouteUrlObject, isRouteUrlObject} from "blitz"

export * from "./index-browser"

Expand Down Expand Up @@ -130,10 +132,12 @@ export const setupBlitzServer = <TPlugins extends readonly BlitzServerPlugin<obj
plugins,
onError,
logger,
formatError,
}: {
plugins: TPlugins
onError?: (err: Error) => void
logger?: ReturnType<typeof BlitzLogger>
formatError?: (err: Error) => Error
}) => {
initializeLogger(logger ?? BlitzLogger())

Expand Down Expand Up @@ -168,9 +172,9 @@ export const setupBlitzServer = <TPlugins extends readonly BlitzServerPlugin<obj
getClient(),
),
)
} catch (err: any) {
onError?.(err)
throw err
} catch (error: any) {
onError?.(error)
throw formatError?.(error) ?? error
}
}

Expand All @@ -197,9 +201,9 @@ export const setupBlitzServer = <TPlugins extends readonly BlitzServerPlugin<obj
getClient(),
),
)
} catch (err: any) {
onError?.(err)
throw err
} catch (error: any) {
onError?.(error)
throw formatError?.(error) ?? error
}
}

Expand All @@ -215,7 +219,8 @@ export const setupBlitzServer = <TPlugins extends readonly BlitzServerPlugin<obj
])
} catch (error: any) {
onError?.(error)
return res.status(400).send(error)
const formattedError = formatError?.(error) ?? error
return res.status(400).send(formattedError)
}
}

Expand Down
6 changes: 4 additions & 2 deletions packages/blitz-rpc/src/index-server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {assert, baseLogger, Ctx, newLine, prettyMs, ResolverConfig} from "blitz"
import {NextApiRequest, NextApiResponse} from "next"
import {deserialize, serialize as superjsonSerialize, parse} from "superjson"
import {deserialize, parse, serialize as superjsonSerialize} from "superjson"
import {resolve} from "path"
import chalk from "chalk"

Expand Down Expand Up @@ -149,6 +149,7 @@ async function getResolverMap(): Promise<ResolverFiles | null | undefined> {

interface RpcConfig {
onError?: (error: Error) => void
formatError?: (error: Error) => Error
}

export function rpcHandler(config: RpcConfig) {
Expand Down Expand Up @@ -265,7 +266,8 @@ export function rpcHandler(config: RpcConfig) {
error.statusCode = 500
}

const serializedError = superjsonSerialize(error)
const formattedError = config.formatError?.(error) ?? error
const serializedError = superjsonSerialize(formattedError)

res.json({
result: null,
Expand Down
9 changes: 3 additions & 6 deletions packages/blitz/src/cli/utils/routes-manifest.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import {join, dirname} from "path"
import {join} from "path"
import os from "os"
import {promises} from "fs"
const readFile = promises.readFile
import {outputFile, readdir} from "fs-extra"
import findUp from "find-up"
import resolveFrom from "resolve-from"
import {outputFile, readdir, readFile} from "fs-extra"
import Watchpack from "watchpack"
import {isInternalBlitzMonorepoDevelopment} from "./helpers"
import {findNodeModulesRoot} from "./find-node-modules"

const debug = require("debug")("blitz")
export const CONFIG_FILE = ".blitz.config.compiled.js"
export const NEXT_CONFIG_FILE = "next.config.js"
Expand Down
2 changes: 1 addition & 1 deletion packages/blitz/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {IncomingMessage, ServerResponse} from "http"
import {compose, Ctx, RequestMiddleware, MiddlewareNext, MiddlewareResponse} from "./index-server"
import {compose, Ctx, MiddlewareNext, MiddlewareResponse, RequestMiddleware} from "./index-server"

export async function handleRequestWithMiddleware<
Req extends IncomingMessage = IncomingMessage,
Expand Down
Loading