Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This does not work with nest js #52

Closed
DenisVASI9 opened this issue Oct 26, 2022 · 12 comments
Closed

This does not work with nest js #52

DenisVASI9 opened this issue Oct 26, 2022 · 12 comments

Comments

@DenisVASI9
Copy link

Error [ERR_REQUIRE_ESM]: require() of ES Module /home/hermione/cd-market-backend/node_modules/pocketbase/dist/pocketbase.es.mjs not supported.
Instead change the require of /home/hermione/cd-market-backend/node_modules/pocketbase/dist/pocketbase.es.mjs to a dynamic import() which is available in all CommonJS modules.

@ganigeorgiev
Copy link
Member

ganigeorgiev commented Oct 26, 2022

I'll test it later today with nest, but most likely this a transpiler (eg. TypeScript) misconfiguration, so make sure that your nest project can load ES modules.

The easiest workaround for your error would be to load the CommonJS PocketBase bundle like:

const PocketBase = require('pocketbase/cjs');

The above should work because it is a native CommonJS module, where the default 'pocketbase' import is ESM.

@DenisVASI9
Copy link
Author

This kind of connection will work, but will block eslint. It doesn't look uniform. I tried to fix it, but my skills are lacking.

@ganigeorgiev
Copy link
Member

ganigeorgiev commented Oct 26, 2022

@DenisVASI9 i'm not sure that I understand what do you mean by " but will block eslint".

PocketBase is ESM by default. The legacy CJS bundle is pocketbase/cjs.

If you want to use CommonJS, use pocketbase/cjs. There is no other way around without extra configuration.

The error indicates that you are using import statements with CommonJS and this is "wrong" since it is just a syntax sugar. What actually is happening under the hood is that TypeScript (or whatever other transpiler you are using) based on the error is generating a plain js file and converts your import .... statements to require(...) (you can inspect the generated nest js files in your /dist folder). This will not work with PocketBase (or any other ESM library).

Currently the nestjs template doesn't seem to come with ESM support by default (see nestjs/nest#10267 (comment), nestjs/nest#8736, etc.). If you want to use ES modules without hacky interops, you'll have to:

  1. Add in you package.json the following line:

    "type": "module",
  2. If you are using TypeScript, enable esnext in your tsconfig.json under the compilerOptions, eg.:

    "module": "esnext",
    "moduleResolution": "node",
    "target": "esnext",
    ...

    (you can learn more about the available module resolution strategies in https://www.typescriptlang.org/docs/handbook/module-resolution.html#module-resolution-strategies)

  3. Add .js suffix to all your local file imports, for example:

    import { AppModule } from './app.module';
    // change to 
    import { AppModule } from './app.module.js';

In any case, this is not related to PocketBase and you'll stumble on the same error if you try to import any other ESM package.

@DenisVASI9
Copy link
Author

Thank you!

@DenisVASI9
Copy link
Author

@ganigeorgiev
I have that error with "pocketbase/cjs": TS2307: Cannot find module 'pocketbase/cjs' or its corresponding type declarations.

@ganigeorgiev
Copy link
Member

@DenisVASI9 What option did you end up using? require("pocketbase/cjs") or you've decided to go with the ESM route?

Once again, this is not related to the SDK and I'm not sure how to help without any information on your setup (eg. tsconfig.json) and how you are loading the library.

My advice would be to apply the listed 3 steps from above and start using ESM and import the SDK just by:

import PocketBase from "pocketbase";

If you want to allow loading other CJS modules with ESM you can also add "esModuleInterop": true, to your tsconfig compilerOptions.

@DenisVASI9
Copy link
Author

DenisVASI9 commented Oct 26, 2022

@ganigeorgiev I've been using

import PocketBase from "pocketbase/cjs"

I was hoping to use cjs without changing the nest tranpiler settings.
Yes, I understand that this is not an sdk problem.
Thank you for your time.

@DenisVASI9
Copy link
Author

DenisVASI9 commented Oct 26, 2022

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "Node",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "lib": ["ESNext"],
    "target": "ESNext",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "strict": true,
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false
  }
}

@ganigeorgiev
Copy link
Member

ganigeorgiev commented Oct 26, 2022

@DenisVASI9 You can use import PocketBase from "pocketbase/cjs" only if you change your moduleResolution to nodenext (there is an open issue for that in the TypeScript repo - microsoft/TypeScript#50794).

Update: You may also need to add "esModuleInterop": true. Or in other words, you could try changing your tsconfig to:

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "nodenext",          <--- changed
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,                 <--- added
    "lib": ["ESNext"],
    "target": "ESNext",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "strict": true,
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false
  }
}

But once again, my recommendation would be to migrate to es modules entirely since you'll stumble on similar errors down the road with other packages that may not have cjs alternative.

Or just use const PocketBase = require("pocketbase/cjs")

@ArjunAtlast
Copy link

Hi, in case someone is stuck with this issue - I was able to do an unorthodox fix for this issue using eval. This method is explained in a StackOverflow answer. Attaching the link to the answer as well.

Compile a package that depends on ESM only library into a CommonJS package

This is my code for a NestJS Service class with pocketbase instance as a property:

import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Cron, CronExpression } from "@nestjs/schedule";
import type Pocketbase from "pocketbase";

@Injectable()
export class PocketbaseService implements OnModuleInit, OnModuleDestroy {
  private _client: Pocketbase;

  constructor(
    private readonly config: ConfigService
  ) {}

  get client() {
    return this._client;
  }

  async onModuleInit() {
    // create client
    const pocketbase = await (eval(`import('pocketbase')`) as Promise<typeof import('pocketbase')>);
    const PocketbaseClass = pocketbase.default;
    this._client = new PocketbaseClass(this.config.get('POCKETBASE_URL'));
    // login as admin
    await this.client.admins.authWithPassword(this.config.get('POCKETBASE_ADMIN_EMAIL'), this.config.get('POCKETBASE_ADMIN_PASSWORD'));
  }

  async onModuleDestroy() {
    // logout
    this.client.authStore.clear();
  }

  @Cron(CronExpression.EVERY_5_MINUTES)
  async reAuth() {
    // re-authenticate every 5 minutes
    await this.client.admins.authRefresh();
  }
}

@laravellously
Copy link

Hi, in case someone is stuck with this issue - I was able to do an unorthodox fix for this issue using eval. This method is explained in a StackOverflow answer. Attaching the link to the answer as well.

Compile a package that depends on ESM only library into a CommonJS package

This is my code for a NestJS Service class with pocketbase instance as a property:

import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Cron, CronExpression } from "@nestjs/schedule";
import type Pocketbase from "pocketbase";

@Injectable()
export class PocketbaseService implements OnModuleInit, OnModuleDestroy {
  private _client: Pocketbase;

  constructor(
    private readonly config: ConfigService
  ) {}

  get client() {
    return this._client;
  }

  async onModuleInit() {
    // create client
    const pocketbase = await (eval(`import('pocketbase')`) as Promise<typeof import('pocketbase')>);
    const PocketbaseClass = pocketbase.default;
    this._client = new PocketbaseClass(this.config.get('POCKETBASE_URL'));
    // login as admin
    await this.client.admins.authWithPassword(this.config.get('POCKETBASE_ADMIN_EMAIL'), this.config.get('POCKETBASE_ADMIN_PASSWORD'));
  }

  async onModuleDestroy() {
    // logout
    this.client.authStore.clear();
  }

  @Cron(CronExpression.EVERY_5_MINUTES)
  async reAuth() {
    // re-authenticate every 5 minutes
    await this.client.admins.authRefresh();
  }
}

Worked perfectly. Thanks.

@adebisi-fa
Copy link

adebisi-fa commented Aug 7, 2024

Hi, in case someone is stuck with this issue - I was able to do an unorthodox fix for this issue using eval. This method is explained in a StackOverflow answer. Attaching the link to the answer as well.

Compile a package that depends on ESM only library into a CommonJS package

This is my code for a NestJS Service class with pocketbase instance as a property:

import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Cron, CronExpression } from "@nestjs/schedule";
import type Pocketbase from "pocketbase";

@Injectable()
export class PocketbaseService implements OnModuleInit, OnModuleDestroy {
  private _client: Pocketbase;

  constructor(
    private readonly config: ConfigService
  ) {}

  get client() {
    return this._client;
  }

  async onModuleInit() {
    // create client
    const pocketbase = await (eval(`import('pocketbase')`) as Promise<typeof import('pocketbase')>);
    const PocketbaseClass = pocketbase.default;
    this._client = new PocketbaseClass(this.config.get('POCKETBASE_URL'));
    // login as admin
    await this.client.admins.authWithPassword(this.config.get('POCKETBASE_ADMIN_EMAIL'), this.config.get('POCKETBASE_ADMIN_PASSWORD'));
  }

  async onModuleDestroy() {
    // logout
    this.client.authStore.clear();
  }

  @Cron(CronExpression.EVERY_5_MINUTES)
  async reAuth() {
    // re-authenticate every 5 minutes
    await this.client.admins.authRefresh();
  }
}

This is gold! Worked so perfectly. Thank you.
PS: Node must be at least v16+.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants