Reputation: 1507
I’m trying to use the Keycloak Admin Client library in my Nodejs (Typescript) application, but there's a problem about ES6/CommonJs stuff, which I never really understood (import vs require and mixing things).
Here is a snippet of my source code:
import [... whatever ...]
import KcAdminClient from '@keycloak/keycloak-admin-client';
import { Credentials } from '@keycloak/keycloak-admin-client/lib/utils/auth';
export class Controller {
private kcAdminClient = new KcAdminClient();
[...]
As soon as I run my application, I get the following error:
Error [ERR_REQUIRE_ESM]: require() of ES Module .../node_modules/@keycloak/keycloak-admin-client/lib/index.js from .../server/logic/auth/users.ts not supported.
Instead change the require of index.js in .../server/logic/auth/users.ts to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (.../server/logic/auth/users.ts:10:49)
at m._compile (.../node_modules/ts-node/dist/index.js:791:29)
at require.extensions.<computed> [as .ts] (.../node_modules/ts-node/dist/index.js:793:16) {
code: 'ERR_REQUIRE_ESM'
}
But my code does not use require(), or, at least, I haven't put any “require” string in my source file, so I assume it has something to do with how Keycloak Amin Client library is built and how I'm used to import libraries without really understanding what I am doing. In fact I'm pretty sure I faced the same problem in the past with another library, but I can't remember the solution, allegedly because I did not understand it even at the time...
Following the error message advice I tried to write a dynamic import, but I need to import a class name and all of my clumsy attempts resulted in syntax errors at compile time.
How should I import such libraries? And what is the meaning of "such" in this case?
Side note: two days ago I posted the same question on one of the official Keycloak communities, but no one even tried to comment, so I'm inclined to believe that the root cause does not lie into the library itself.
EDIT after Bench Vue answer:
I tried to add "type": "module"
to my package.json as suggested, but then I got a different error:
TypeError: Unknown file extension ".ts"
which led me to another SO Question, where the accepted and most upvoted answer suggests to remove "type": "module"
from package.json, or alternatively, to add "esModuleInterop": true
to the compilerOptions
in my tsconfig.json, which was already there, but issuing ts-node-esm server/index.ts
in a terminal still yelds the same error (ERR_UNKNOWN_FILE_EXTENSION).
Upvotes: 2
Views: 1111
Reputation: 19
You can use dynamic import. Below is sample code
import KcAdminClient from '@keycloak/keycloak-admin-client'
import { Injectable } from '@nestjs/common'
@Injectable()
export class KeycloakService {
kcAdminClient: KcAdminClient
constructor() {
setTimeout(async () => {
this.run()
}, 1000)
}
async run() {
this.kcAdminClient = new (await this.dynamicKeycloakImport()).default()
await this.kcAdminClient.setConfig({
baseUrl: 'https://keycloak.dev.covergo.cloud/',
realmName: 'distribution_dev',
})
const credentials = {
grantType: 'client_credentials',
clientId: 'client-id',
clientSecret: 'secret',
}
await this.kcAdminClient.auth(credentials as any)
const users = await this.kcAdminClient.users.find({ first: 0, max: 10 })
console.log(users)
}
private dynamicKeycloakImport = async () =>
new Function("return import('@keycloak/keycloak-admin-client')")()
}
Upvotes: 1
Reputation: 9300
You can use only "module" in your packages.json
"type": "module"
The reason
the type field in the package.json
of the @keycloak/keycloak-admin-client
package indicates that it is designed to be used as an ECMAScript module (ESM).
The error message you encountered suggests that the Keycloak Admin Client library is an ECMAScript module (ESM), which means it should be imported using import statements, not require().
Since you didn't explicitly use require() in your code, the problem lies in how you're importing the Keycloak Admin Client library. You might be using require() implicitly, or there could be some configuration issue causing Node.js to interpret the import as a CommonJS module.
To resolve this issue, make sure you're importing the Keycloak Admin Client library using import statements, and ensure that your project's configuration (package.json) is set up to use ECMAScript modules ("type": "module").
import KcAdminClient from '@keycloak/keycloak-admin-client';
const kcAdminClient = new KcAdminClient(
{
baseUrl: 'http://127.0.0.1:8080',
realmName: 'master'
}
);
// Authorize with username / password
await kcAdminClient.auth({
username: 'admin',
password: 'admin',
grantType: 'password',
clientId: 'admin-cli',
});
// List first page of users
const users = await kcAdminClient.users.find({ first: 0, max: 10 });
console.log(JSON.stringify(users, null, 4));
In packages.json
{
"type": "module",
"dependencies": {
"@keycloak/keycloak-admin-client": "^23.0.7"
}
}
Save as 'demo-user.ts'
import KcAdminClient from '@keycloak/keycloak-admin-client';
const kcAdminClient = new KcAdminClient(
{
baseUrl: 'http://127.0.0.1:8080',
realmName: 'master'
}
);
async function main() {
// Authorize with username / password
await kcAdminClient.auth({
username: 'admin',
password: 'admin',
grantType: 'password',
clientId: 'admin-cli',
});
// List first page of users
const users = await kcAdminClient.users.find({ first: 0, max: 10 });
console.log(JSON.stringify(users, null, 4));
}
main();
Save as tsconfig.json
{
"compilerOptions": {
"target": "es2015",
"module": "ESNext",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node"
}
}
I tried es2017
and es2020
but it did not work.
In package.json
{
"type": "module",
"dependencies": {
"@keycloak/keycloak-admin-client": "^23.0.7",
"typescript": "^5.3.3"
}
}
-p
or --project
option with tsc
, TypeScript will compile all TypeScript files in the project directory and its subdirectories according to the configuration specified in the tsconfig.json file
tsc -p ./tsconfig.json
It will generate demo-user.js
file from demo-user.ts
node demo-user.js
Upvotes: 0