Skip to content

Commit

Permalink
Add experimantal singleRouteNextJsHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh committed Feb 12, 2025
1 parent cd547e2 commit 7ded30d
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 12 deletions.
11 changes: 4 additions & 7 deletions src/Fastify.res
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ let route = (app: t, restRoute: Rest.route<'request, 'response>, fn) => {
// 1. To be able to configure ContentTypeParser specifically for the route
// 2. To get access to app with registered plugins eg Swagger
app->internalRegister((app, _, done) => {
let {definition, variablesSchema, responses, pathItems, isRawBody} = restRoute->Rest.params
let {definition, variablesSchema, responseSchema, responses, pathItems, isRawBody} =
restRoute->Rest.params

let url = ref("")
for idx in 0 to pathItems->Js.Array2.length - 1 {
Expand All @@ -205,10 +206,8 @@ let route = (app: t, restRoute: Rest.route<'request, 'response>, fn) => {
}
}

let responseSchemas = []
let routeSchemaResponses: dict<routeResponse> = Js.Dict.empty()
responses->Js.Array2.forEach(r => {
responseSchemas->Js.Array2.push(r.schema)->ignore
let status = switch r.status {
| Some(status) => status->(Obj.magic: int => string)
| None => "default"
Expand All @@ -235,8 +234,6 @@ let route = (app: t, restRoute: Rest.route<'request, 'response>, fn) => {
)
})

let responseSchema = S.union(responseSchemas)

let routeSchema = {
description: ?definition.description,
summary: ?definition.summary,
Expand All @@ -261,8 +258,8 @@ let route = (app: t, restRoute: Rest.route<'request, 'response>, fn) => {
raise(%raw(`0`))
}
}
fn(variables)->Promise.thenResolve(handlerReturn => {
let data: {..} = handlerReturn->S.reverseConvertOrThrow(responseSchema)->Obj.magic
fn(variables)->Promise.thenResolve(implementationResult => {
let data: {..} = implementationResult->S.reverseConvertOrThrow(responseSchema)->Obj.magic
let headers = data["headers"]
if headers->Obj.magic {
reply.headers(headers)
Expand Down
8 changes: 3 additions & 5 deletions src/Fastify.res.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions src/Rest.res
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ type routeParams<'variables, 'response> = {
definition: definition<'variables, 'response>,
pathItems: array<pathItem>,
variablesSchema: S.t<'variables>,
responseSchema: S.t<'response>,
responses: array<Response.t<'response>>,
responsesMap: dict<Response.t<'response>>,
isRawBody: bool,
Expand Down Expand Up @@ -526,6 +527,7 @@ let params = route => {
let params = {
definition: routeDefinition,
variablesSchema,
responseSchema: S.union(responses->Js.Array2.map(r => r.schema)),
responses,
pathItems,
responsesMap,
Expand Down Expand Up @@ -720,3 +722,57 @@ let client = (~baseUrl, ~fetcher=ApiFetcher.default, ~jsonQuery=false) => {
jsonQuery,
}
}

type nextJsHandler

let singleRouteNextJsHandler = (route, implementation): nextJsHandler => {
let {pathItems, definition, isRawBody, responseSchema, variablesSchema} = route->params

// TOD: Validate that we match the req path
pathItems->Js.Array2.forEach(pathItem => {
switch pathItem {
| Param(param) =>
panic(
`Route ${definition.path} contains a path param ${param.name} which is not supported by Next.js handler yet`,
)
| Static(_) => ()
}
})
if isRawBody {
panic(
`Route ${definition.path} contains a raw body which is not supported by Next.js handler yet`,
)
}

(
async (req, res) => {
if req["method"] !== definition.method {
res["status"](404)["end"]()
}
switch req->S.parseOrThrow(variablesSchema) {
| variables =>
try {
let implementationResult = await implementation(variables)
let data: {..} = implementationResult->S.reverseConvertOrThrow(responseSchema)->Obj.magic
let headers: option<dict<string>> = data["headers"]
switch headers {
| Some(headers) =>
headers
->Js.Dict.keys
->Js.Array2.forEach(key => {
res["setHeader"](key, headers->Js.Dict.unsafeGet(key))
})
| None => ()
}
res["status"](%raw(`data.status || 200`))
} catch {
| S.Raised(error) =>
Js.Exn.raiseError(
`Unexpected error in the ${definition.path} route: ${error->S.Error.message}`,
)
}
| exception S.Raised(error) => res["status"](400)["send"](error->S.Error.message)
}
}
)->Obj.magic
}
56 changes: 56 additions & 0 deletions src/Rest.res.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/Rest.resi
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ type routeParams<'variables, 'response> = {
definition: definition<'variables, 'response>,
pathItems: array<pathItem>,
variablesSchema: S.t<'variables>,
responseSchema: S.t<'response>,
responses: array<Response.t<'response>>,
responsesMap: dict<Response.t<'response>>,
isRawBody: bool,
Expand All @@ -175,3 +176,9 @@ let fetch: (
~fetcher: ApiFetcher.t=?,
~jsonQuery: bool=?,
) => promise<'response>

type nextJsHandler
let singleRouteNextJsHandler: (
route<'variables, 'response>,
'variables => promise<'response>,
) => nextJsHandler

0 comments on commit 7ded30d

Please sign in to comment.