brillout / telefunc

Remote Functions. Instead of API.
https://telefunc.com
MIT License
688 stars 31 forks source link

Setting up telefunc + vite-plugin-ssr inside a NestJS project #82

Closed stampaaaron closed 1 year ago

stampaaaron commented 1 year ago

For an admin UI of one of our projects I'm trying to setup a SSR vite application inside a module of an existing NestJS project.

For that I was trying to make this example of yours work inside my nest environment: https://github.com/brillout/telefunc/tree/main/examples/vite-plugin-ssr

The Idea is to use the same express server as the NestJS application and use the vite middleware on it.

NestJS Server Setup:

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';
import vite from 'vite';
import { config } from 'telefunc';

const adminRoot = join(__dirname, '..', 'src/admin/dashboard');

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule, {
    rawBody: true,
  });
  app.useStaticAssets(join(__dirname, '..', 'public'));

  app.useBodyParser('text');

  const viteDevMiddleware = (
    await vite.createServer({
      root: adminRoot,
      server: { middlewareMode: true },
    })
  ).middlewares;
  app.use(viteDevMiddleware);

  config.telefuncUrl = '/admin/dashboard/_telefunc';

  await app.listen(3000);
}
bootstrap();

Instead of configuring the endpoints needed for telefunc and vite-plugin-ssr directly on the express server, I was hoping to add those to a NestJS Controller.

DashboardController:

import { Controller, Get, Post, Req, Res } from '@nestjs/common';
import { DashbaordService } from './dashboard.service';
import { Request, Response } from 'express';

@Controller('admin/dashboard')
export class DashboardController {
  constructor(private dashboardService: DashbaordService) {}

  @Get('/')
  async renderIndex(@Req() req: Request, @Res() res: Response) {
    return this.dashboardService.renderDashboard(req, res);
  }

  @Get('*')
  async renderPages(@Req() req: Request, @Res() res: Response) {
    return this.dashboardService.renderDashboard(req, res);
  }

  @Post('_telefunc')
  async telefunc(@Req() req: Request, @Res() res: Response) {
    return this.dashboardService.handleTelefuncRequest(req, res);
  }
}

DashboardService:

import { Injectable } from '@nestjs/common';
import { Request, Response } from 'express';
import { telefunc } from 'telefunc';
import { renderPage } from 'vite-plugin-ssr/server';

@Injectable()
export class DashbaordService {
  async renderDashboard(req: Request, res: Response) {
    const pageContextInit = {
      urlOriginal: req.originalUrl,
    };

    const pageContext = await renderPage(pageContextInit);
    const { httpResponse } = pageContext;

    if (!httpResponse) return;

    const { body, statusCode, headers, earlyHints } = httpResponse;

    if (res.writeEarlyHints)
      res.writeEarlyHints({
        link: earlyHints.map((e) => e.earlyHintLink),
      });

    headers.forEach(([name, value]) => res.setHeader(name, value));
    res.status(statusCode);

    return res.send(body);
  }

  async handleTelefuncRequest(req: Request, res: Response) {
    const context = {};
    const { originalUrl: url, method, body } = req;

    const httpResponse = await telefunc({ url, method, body, context });

    const { body: responseBody, statusCode, contentType } = httpResponse;

    return res.status(statusCode).type(contentType).send(responseBody);
  }
}

Like this I managed to get the vite-plugin-ssr working (which is awesome already :)).

With telefunc I got the following Error when I was trying to do a request:

[vite] Error when evaluating SSR module [...]/node_modules/telefunc/dist/cjs/node/vite/importGlob/telefuncFilesGlob.js:
|- ReferenceError: exports is not defined
    at eval ([...]/node_modules/telefunc/dist/cjs/node/vite/importGlob/telefuncFilesGlob.js:4:23)
    at instantiateModule (file:///[...]/node_modules/vite/dist/node/chunks/dep-df561101.js:55974:15)
ReferenceError: exports is not defined
    at [...]/node_modules/telefunc/dist/cjs/node/vite/importGlob/telefuncFilesGlob.js:2:23
    at instantiateModule (file:///[...]/node_modules/vite/dist/node/chunks/dep-df561101.js:55974:15)

After this I tried to add the vite-plugin-commonjs to the vite configuration, because I thought it might have something to do with NestJS still using commonjs.

Vite Configuration:

import react from '@vitejs/plugin-react';
import ssr from 'vite-plugin-ssr/plugin';
import { UserConfig } from 'vite';
import { telefunc } from 'telefunc/vite';
import commonjs from 'vite-plugin-commonjs';

const config: UserConfig = {
  base: '/admin/dashbaord/',
  plugins: [
    commonjs({
      filter(id) {
        if (id.includes('node_modules/telefunc')) {
          return true;
        }
      },
    }),
    telefunc(),
    react(),
    ssr({
      baseServer: '/admin/dashboard/',
    }),
  ],
};

export default config;

This seemed to have fixed my original Error from before, but it lead me to this new Error:

Error: [telefunc@0.1.59][Bug] You stumbled upon a bug in Telefunc's source code. Reach out at https://github.com/brillout/telefunc/issues/new or https://discord.com/invite/3DYWwk4xRQ and include this error stack (the error stack is usually enough to fix the problem). A maintainer will fix the bug (usually under 24 hours). Don't hesitate to reach out as it makes Telefunc more robust. Debug info (this is for the Telefunc maintainers; you can ignore this): `{"moduleExports":{"importGlobUnset":true},"viteProvider":"viteDevServer"}`.
    at loadTelefuncFilesWithVite ([...]/node_modules/telefunc/dist/cjs/node/vite/loadTelefuncFilesWithVite.js:12:24)
    at loadTelefuncFiles ([...]/node_modules/telefunc/dist/cjs/node/server/runTelefunc/loadTelefuncFiles.js:34:73)
    at runTelefunc_ ([...]/node_modules/telefunc/dist/cjs/node/server/runTelefunc.js:82:59)
    at runTelefunc ([...]/node_modules/telefunc/dist/cjs/node/server/runTelefunc.js:38:16)
    at telefunc ([...]/node_modules/telefunc/dist/cjs/node/server/telefunc.js:11:26)
    at DashbaordService.handleTelefuncRequest ([...]/src/admin/dashboard.service.ts:35:26)
    at [...]/node_modules/@nestjs/core/router/router-execution-context.js:46:28
    at [...]/node_modules/@nestjs/core/router/router-proxy.js:9:17

At this point, I don't know how to proceed with this internal error. Does this still have something to do with commonjs/esm?

I'm a big fan of the concept of telefunc and I would really appreciate your support. Thanks in advance!

brillout commented 1 year ago

[vite] Error when evaluating SSR module [...]/node_modules/telefunc/dist/cjs/node/vite/importGlob/telefuncFilesGlob.js

That's the culprit: node_modules/telefunc/ shouldn't be processed by Vite. Did you add Telefunc to Vite's ssr.noExternal?

stampaaaron commented 1 year ago

Thanks for the quick reply. Haven't tried that, but it doesn't seem to change anything, I still get the error.

I tried it with just telefunc and node_modules/telefunc:

  ssr: {
    noExternal: ['node_modules/telefunc'],
  },
brillout commented 1 year ago

Make Sure Vite doesn't process node_modules/telefunc/ (which is Vite's default behavior).

Alternertively, provide a minimal reproduction and I'll have a look at it.

Closing in the meantime.