import { PostGraphileAmberPreset } from "postgraphile/presets/amber";
import { makePgService } from "postgraphile/adaptors/pg";
import { readFileSync } from "node:fs";
const { version } = JSON.parse(readFileSync("node_modules/ruru/package.json"));
/** @type {GraphileConfig.Preset} */
const preset = {
extends: [PostGraphileAmberPreset],
pgServices: [makePgService({ connectionString: "postgres:///todo" })],
grafserv: {
watch: true,
graphqlPath: "/api/graphql",
eventStreamPath: "/api/graphql/stream",
graphiqlPath: "/api/graphiql",
graphiqlStaticPath: `https://unpkg.com/ruru@${version}/static/`,
},
};
export default preset;
import type { NextApiRequest, NextApiResponse } from "next";
import { Readable } from "node:stream";
import {
convertHandlerResultToResult,
GrafservBase,
normalizeRequest,
processHeaders,
} from "grafserv";
import type {
EventStreamHandlerResult,
GrafservBodyBuffer,
GrafservBodyJSON,
GrafservConfig,
RequestDigest,
Result,
} from "grafserv";
declare global {
namespace Grafast {
interface RequestContext {
nextv16: {
req: NextApiRequest;
res: NextApiResponse;
};
}
}
}
function getDigest(req: NextApiRequest, res: NextApiResponse): RequestDigest {
return {
httpVersionMajor: req.httpVersionMajor,
httpVersionMinor: req.httpVersionMinor,
isSecure: "encrypted" in req.socket ? !!req.socket.encrypted : false,
method: req.method!,
path: req.url!,
headers: processHeaders(req.headers),
getQueryParams() {
return req.query as Record<string, string | string[]>;
},
async getBody() {
const body = req.body;
if (Buffer.isBuffer(body)) {
return {
type: "buffer",
buffer: body,
} as GrafservBodyBuffer;
} else if (typeof body === "object" && body !== null) {
return {
type: "json",
json: body,
} as GrafservBodyJSON;
} else {
throw new Error("Failed to retrieve body from next");
}
},
requestContext: {
nextv16: {
req,
res,
},
},
};
}
export class NextGrafserv extends GrafservBase {
constructor(config: GrafservConfig) {
super(config);
}
public async send(
req: NextApiRequest,
res: NextApiResponse,
result: Result | null,
) {
if (result === null) {
return res.status(404).send("¯\\_(ツ)_/¯");
}
switch (result.type) {
case "error": {
const { statusCode, headers } = result;
res
.setHeaders(new Map(Object.entries(headers)))
.status(statusCode)
.json({ errors: [result.error] });
return;
}
case "buffer": {
const { statusCode, headers, buffer } = result;
res
.setHeaders(new Map(Object.entries(headers)))
.status(statusCode)
.send(buffer);
return;
}
case "json": {
const { statusCode, headers, json } = result;
res
.setHeaders(new Map(Object.entries(headers)))
.status(statusCode)
.json(json);
return;
}
case "noContent": {
const { statusCode, headers } = result;
res
.setHeaders(new Map(Object.entries(headers)))
.status(statusCode)
.send("");
return;
}
case "bufferStream": {
const { statusCode, headers, lowLatency, bufferIterator } = result;
res
.setHeaders(new Map(Object.entries(headers)))
.status(statusCode)
.send(Readable.from(bufferIterator));
return;
}
default: {
const never: never = result;
console.log("Unhandled:");
console.dir(never);
res
.setHeaders(
new Map(
Object.entries({
"Content-Type": "text/plain",
}),
),
)
.status(501)
.send("Server hasn't implemented this yet");
return;
}
}
}
public createGraphQLHandler() {
return async (req: NextApiRequest, res: NextApiResponse) => {
const digest = getDigest(req, res);
const handlerResult = await this.graphqlHandler(
normalizeRequest(digest),
this.graphiqlHandler,
);
const result = await convertHandlerResultToResult(handlerResult);
return this.send(req, res, result);
};
}
public createGraphiQLHandler() {
return async (req: NextApiRequest, res: NextApiResponse) => {
const digest = getDigest(req, res);
const handlerResult = await this.graphiqlHandler(
normalizeRequest(digest),
);
const result = await convertHandlerResultToResult(handlerResult);
return this.send(req, res, result);
};
}
public createGraphQLEventStreamHandler() {
return async (req: NextApiRequest, res: NextApiResponse) => {
const digest = getDigest(req, res);
const handlerResult: EventStreamHandlerResult = {
type: "event-stream",
request: normalizeRequest(digest),
dynamicOptions: this.dynamicOptions,
payload: this.makeStream(),
statusCode: 200,
};
const result = await convertHandlerResultToResult(handlerResult);
return this.send(req, res, result);
};
}
}
export function grafserv(config: GrafservConfig) {
return new NextGrafserv(config);
}
Hacked this together quickly: