Last active
August 2, 2021 21:52
-
-
Save giuseppeg/45483f1d4b0e5e5ddd10b3f1eb0a61b6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { NextApiRequest as DefaultNextApiRequest, NextApiResponse } from "next"; | |
/** | |
* // Route /pages/api/posts/[id] | |
* | |
* const api = defineApi<{ id: string }>() | |
* .get<{ query: { foo: "1" } }, "foo" | "bar">( | |
* async function handler(req, res) { | |
* const session = await getSession({ req }); | |
* if (!session) { | |
* throw new ApiError(401, "unauthorized") | |
* } | |
* | |
* return { | |
req.query.foo === "1" ? "foo" : "bar" | |
* } | |
* } | |
* ) | |
* .post<{body:{foo: "hi"}}, void>( | |
* async function handler(req, res) { | |
* req.body.foo | |
* } | |
* ) | |
* | |
* export default api.create() | |
* | |
*/ | |
export type ApiResponse< | |
ResponseData = Record<string, unknown>, | |
ResponseError = { status: number; message: string } | |
> = | |
| { result: true; data: ResponseData } | |
| { result: false; data: ResponseError }; | |
type NextApiRequest<RequestOwnTypings> = Omit< | |
DefaultNextApiRequest, | |
"query" | "body" | |
> & | |
RequestOwnTypings; | |
type NextApiHandler< | |
RequestOwnTypings, | |
ResponseDataOwnTypings, | |
ResponseErrorOwnTypings | |
> = ( | |
req: NextApiRequest<RequestOwnTypings>, | |
res: NextApiResponse< | |
ApiResponse<ResponseDataOwnTypings, ResponseErrorOwnTypings> | |
> | |
) => ResponseDataOwnTypings | Promise<ResponseDataOwnTypings>; | |
export const defineApi = <RouteParams extends Record<string, string>>() => { | |
const handlers: { [key: string]: NextApiHandler<unknown, unknown, unknown> } = | |
{}; | |
const methodHandler = { | |
get: <ApiRequest, ResponseData, ResponseError = { message: string }>( | |
handler: NextApiHandler< | |
ApiRequest & { query: RouteParams }, | |
ResponseData, | |
ResponseError | |
> | |
) => { | |
handlers["GET"] = handler; | |
return methodHandler; | |
}, | |
post: <ApiRequest, ResponseData, ResponseError = { message: string }>( | |
handler: NextApiHandler< | |
ApiRequest & { query: RouteParams }, | |
ResponseData, | |
ResponseError | |
> | |
) => { | |
handlers["POST"] = handler; | |
return methodHandler; | |
}, | |
put: <ApiRequest, ResponseData, ResponseError = { message: string }>( | |
handler: NextApiHandler< | |
ApiRequest & { query: RouteParams }, | |
ResponseData, | |
ResponseError | |
> | |
) => { | |
handlers["PUT"] = handler; | |
return methodHandler; | |
}, | |
delete: <ApiRequest, ResponseData, ResponseError = { message: string }>( | |
handler: NextApiHandler< | |
ApiRequest & { query: RouteParams }, | |
ResponseData, | |
ResponseError | |
> | |
) => { | |
handlers["DELETE"] = handler; | |
return methodHandler; | |
}, | |
create: () => async (req, res) => { | |
if (typeof handlers[req.method] === "function") { | |
try { | |
const data = await handlers[req.method](req, res); | |
if (data) { | |
res.json({ result: true, data }); | |
} | |
if (data === null) { | |
res.json({ result: true, data: {} }); | |
} | |
} catch (error) { | |
const status = error instanceof ApiError ? error.status : 500; | |
const message = | |
error instanceof ApiError || process.env.NODE_ENV === "development" | |
? error.message || "unknown error" | |
: "unknown error"; | |
res.status(status).json({ result: false, data: { status, message } }); | |
} | |
} else { | |
res | |
.status(404) | |
.json({ result: false, data: { status: 404, message: "not found" } }); | |
} | |
}, | |
}; | |
return methodHandler; | |
}; | |
export class ApiError extends Error { | |
status: number; | |
constructor(status: number, message: string) { | |
super(message); | |
this.status = status; | |
} | |
} | |
export function swrFetcher(url: string, options = {}) { | |
return fetch(url, { credentials: "same-origin", ...options }) | |
.then((response) => response.json()) | |
.then(({ result, data }) => { | |
if (!result) { | |
throw new ApiError(data.status, data.message); | |
} | |
return data; | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment