|  | 
|  | 1 | +import { DbClientContract } from '@zenstackhq/runtime'; | 
|  | 2 | +import { HttpException, Inject, Injectable, Scope,  } from "@nestjs/common"; | 
|  | 3 | +import { HttpAdapterHost, REQUEST,  } from "@nestjs/core"; | 
|  | 4 | +import { loadAssets } from "../shared"; | 
|  | 5 | +import { RPCApiHandler } from '../api/rpc'; | 
|  | 6 | +import { ENHANCED_PRISMA } from "./zenstack.constants"; | 
|  | 7 | +import { ApiHandlerOptions } from './interfaces'; | 
|  | 8 | + | 
|  | 9 | +/** | 
|  | 10 | + * The ZenStack API handler service for NestJS. The service is used to handle API requests | 
|  | 11 | + * and forward them to the ZenStack API handler. It is platform agnostic and can be used | 
|  | 12 | + * with any HTTP adapter. | 
|  | 13 | + */ | 
|  | 14 | +@Injectable({scope: Scope.REQUEST}) | 
|  | 15 | +export class ApiHandlerService { | 
|  | 16 | +    constructor(private readonly httpAdapterHost: HttpAdapterHost, @Inject(ENHANCED_PRISMA) private readonly prisma: DbClientContract, @Inject(REQUEST) private readonly request: unknown) {} | 
|  | 17 | + | 
|  | 18 | +    async handleRequest(options?: ApiHandlerOptions): Promise<unknown> { | 
|  | 19 | +        const { modelMeta, zodSchemas } = loadAssets(options || {}); | 
|  | 20 | +        const requestHandler = options?.handler || RPCApiHandler(); | 
|  | 21 | +        const hostname = this.httpAdapterHost.httpAdapter.getRequestHostname(this.request); | 
|  | 22 | +        const requestUrl = this.httpAdapterHost.httpAdapter.getRequestUrl(this.request); | 
|  | 23 | +        // prefix with http:// to make a valid url accepted by URL constructor | 
|  | 24 | +        const url = new URL(`http://${hostname}${requestUrl}`); | 
|  | 25 | +        const method = this.httpAdapterHost.httpAdapter.getRequestMethod(this.request); | 
|  | 26 | +        const path = options?.baseUrl && url.pathname.startsWith(options.baseUrl) ? url.pathname.slice(options.baseUrl.length) : url.pathname; | 
|  | 27 | +        const searchParams = url.searchParams; | 
|  | 28 | +        const query = Object.fromEntries(searchParams); | 
|  | 29 | +        const requestBody = (this.request as {body: unknown}).body; | 
|  | 30 | + | 
|  | 31 | +        const response =  await requestHandler({ | 
|  | 32 | +            method, | 
|  | 33 | +            path, | 
|  | 34 | +            query, | 
|  | 35 | +            requestBody, | 
|  | 36 | +            prisma: this.prisma, | 
|  | 37 | +            modelMeta, | 
|  | 38 | +            zodSchemas, | 
|  | 39 | +            logger: options?.logger, | 
|  | 40 | +        }); | 
|  | 41 | +        console.log(response) | 
|  | 42 | +        // handle handler error | 
|  | 43 | +        // if reponse code >= 400 throw nestjs HttpException | 
|  | 44 | +        // the error response will be generated by nestjs | 
|  | 45 | +        // caller can use try/catch to deal with this manually also | 
|  | 46 | +        if (response.status >= 400) { | 
|  | 47 | +            // eslint-disable-next-line @typescript-eslint/no-explicit-any | 
|  | 48 | +            throw new HttpException(response.body as Record<string, any>, response.status) | 
|  | 49 | +        } | 
|  | 50 | +        return response.body | 
|  | 51 | +    } | 
|  | 52 | +} | 
0 commit comments