proc newClient(opts: JsObject): JsObject {.importjs: "new Client(#)".} Run
or proc newClient(intents: JsObject): JsObject {.importjs: "new Client({intents: #})".} Run I'd probably do something like this: import std/[jsconsole, asyncjs] {.emit: "import { Client } from 'discord.js';".} type Client = ref object user: User User = ref object tag: cstring GatewayIntentBits = enum Guilds = 1 GuildMembers = 2 # ... etc Interaction = ref object commandName: cstring proc newClient(intents: seq[GatewayIntentBits]): Client {.importjs: "new Client({intents: #})".} proc onReady(client: Client; callback: proc()) {.importjs: "#.on('ready', #)".} proc onInteractionCreate(client: Client; callback: proc(interaction: Interaction) {.async.}) {.importjs: "#.on('interactionCreate', #)".} proc login(client: Client; token: cstring) {.importjs: "#.login(#)".} proc isChatInputCommand(interaction: Interaction): bool {.importjs: "#.isChatInputCommand()".} proc reply(interaction: Interaction; msg: cstring): Future[void] {.importjs: "#.reply(#)".} let client = newClient(@[GatewayIntentBits.Guilds]) client.onReady(proc = console.log("Logged in as", client.user.tag, "!") ) client.onInteractionCreate(proc(interaction: Interaction) {.async.} = if not interaction.isChatInputCommand(): return if interaction.commandName == "ping": await interaction.reply("Pong!") ) # client.login(TOKEN) # in the example TOKEN is not defined Run Stronger typing for wrapping a library instead of using jsffi/jsobject for all types. Compile with `nim js -d:danger` when you're checking the codegen of your wrapper to make sure it produces the js you expect (the output will be less noisy from bounds checking etc)