Created
July 16, 2020 07:56
-
-
Save misterjohannesson/af465e6f3f904675be48cc620b41a604 to your computer and use it in GitHub Desktop.
NextJS API Pages server-side wrapper
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 post from "pages/api/post"; | |
import serverApiHandler from "utils/serverApiHandler"; | |
const IdExample = () => <div></div> | |
IdExample.getInitialProps = async ctx => { | |
const { query } = ctx; | |
let id = query.id; | |
let data = null; | |
if (process.browser) { | |
data = await fetch("/api/post", { | |
method: "POST", | |
body: JSON.stringify({ name: id }) | |
}).then(r => r.json()); | |
} else { | |
data = await serverApiHandler(ctx, post, {}, "POST", { name: id }); | |
} | |
return { | |
source: process.browser ? "browser" : "server", | |
data | |
}; | |
}; | |
export default IdExample; |
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 { ServerResponse, IncomingMessage, IncomingHttpHeaders } from "http"; | |
import { ParsedUrlQuery } from "querystring"; | |
class _ServerApiResponse { | |
constructor(res: ServerResponse) { | |
const proto = { ..._ServerApiResponse.prototype }; | |
Object.assign(proto, Object.getPrototypeOf(res)); | |
Object.setPrototypeOf(this, proto); | |
Object.assign(this, res); | |
} | |
statusCode: number; | |
statusMessage: string; | |
data: any; | |
status = (int: number) => { | |
this.statusCode = int; | |
return this; | |
}; | |
send = (obj: any) => { | |
this.data = obj; | |
}; | |
json = (obj: any) => { | |
this.data = obj; | |
}; | |
setPreviewData = (data: string | object, options?: { maxAge?: number }) => { | |
throw Error("NOT YET IMPLEMENTED"); | |
return this; | |
}; | |
clearPreviewData = () => { | |
throw Error("NOT YET IMPLEMENTED"); | |
return this; | |
}; | |
} | |
type ServerApiResponse = _ServerApiResponse & ServerResponse; | |
export const ServerApiResponse: new ( | |
data: ServerResponse | |
) => ServerApiResponse = _ServerApiResponse as any; | |
export class _ServerApiRequest { | |
constructor(req: IncomingMessage, query: ParsedUrlQuery = {}) { | |
Object.assign(this, req); | |
this.query = query; | |
this.cookies = this.headers.cookie | |
?.split("; ") | |
.reduce<{ [key: string]: string }>((acc, set) => { | |
const [key, value] = set.split("="); | |
acc[key] = value; | |
return acc; | |
}, {}); | |
} | |
headers: IncomingHttpHeaders; | |
query; | |
cookies; | |
body; | |
env; | |
} | |
type ServerApiRequest = _ServerApiRequest & IncomingMessage; | |
export const ServerApiRequest: new ( | |
req: IncomingMessage, | |
query: ParsedUrlQuery | |
) => ServerApiRequest = _ServerApiRequest as any; |
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 { NextApiHandler, NextPageContext } from "next"; | |
import { ServerApiRequest, ServerApiResponse } from "types/ServerApiHandler"; | |
/** | |
* This helper function wraps calling the api pages when on the server side. | |
* To avoid having to construct a network call from the server to the server. | |
* | |
* Pass the Next Page Context from getInitialProps or getServerProps | |
* and the default export api page. | |
* It is possible to mock a POST or a PUT, simply add the method param and the possible body. | |
* | |
* Example: | |
* ```typescript | |
* import post from "pages/api/post"; | |
* //... | |
* Component.getInitialProps = async ctx => { | |
* if (!process.browser) { | |
* const getData = await serverApiHandler(ctx, post); | |
* const postData = await serverApiHandler(ctx, post, {}, "POST", { name: id }); | |
* ``` | |
* | |
* @param context: NextPageContext | |
* @param fn: NextApiHandler | |
* @param query: object | |
* @param method: string | |
* @param body: any | |
*/ | |
export default async ( | |
{ req, res }: NextPageContext, | |
fn: NextApiHandler, | |
query: { [key: string]: string } = {}, | |
method: "GET" | "POST" | "PUT" | "DELETE" = "GET", | |
body?: any | |
) => { | |
if (process.browser) { | |
throw Error("Server API Handler can only be used server-side"); | |
} | |
//Create mock instances of the HTTP request and response. | |
const request = new ServerApiRequest(req, query); | |
const response = new ServerApiResponse(res); | |
request.method = method; | |
if (body) request.body = body; | |
//Execute the passed api page function. (this includes middleware) | |
await fn(request, response); | |
console.debug("Server fetched API data", response.data); | |
return response.data; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment