Getting Data from .env File
I created a new Sapphire app using the CLI. I am using swc. And, I want to ask how do access env variables in my bot? The project contains a
src/.env
file . So, naturally, I thought to put my discord token there and access it in my src/index.ts
file as follows.
However, running this throws an error : Error [TokenInvalid]: An invalid token was provided.
. However, it runs if I pass the discord token as a string literal to the client.login()
method. So, this tells me the src/.env
file isn't getting included in the compiled source code. And, upon reviewing the outputted 'dist
folder, it appears this is the case.
How would I fix this? Or, is there a different way I am supposed to access environment variables?Solution:Jump to solution
which is why i was saying to try
export const rootDir = join(__dirname, '..', '..', '..');
89 Replies
Node.js — How to read environment variables from Node.js
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
Can be done with Node 20 and above
Or use this
https://www.npmjs.com/package/dotenv
npm
dotenv
Loads environment variables from .env file. Latest version: 16.4.5, last published: 11 days ago. Start using dotenv in your project by running
npm i dotenv
. There are 40121 other projects in the npm registry using dotenv.On a side note, Sapphire CLI generates a bot template for you to use which takes care of loading env files 🤔
I am using the template Sapphire CLI generated. How do I get it to load the env files like you said?
The generated project structure came with a
sec/.env
file. So I put all my env vars there (discord token, FireBase config info, etc…).
The Sapphire docs didn’t mention anything about handling .env variables. How could I access the values from the env file?env things get loaded to process.env
so if you named your discord token "DISCORD_TOKEN" inside of your env file, you an access it with
process.env.DISCORD_TOKEN
if youre using one of the templates, they should come with @skyra/env-utilities
which handles the loading of the env file, which is done inside of src/lib/setup.{.js | .ts}
Yeah. I saw the code in the
setup.ts
that loads the .env. Let me check my config again.
On a side note, is it normal for the dist
output directory to not contain the .env file from src/.env
? Because mine doesn’t. And I am tempted to think that could be the source of my error.
There was also a little error in the template because it was looking for “dist/index.js” to start the bot, when the compiled output actually had it in dist/src/index.js
. I fixed that by just changing the script in package.json.that is normal
kind of odd how it put it stuff into
dist/src
though isntead of just dist/
im assuming that's something with your tsconfigI haven’t changed anything in the tsconfig.
Only thing I set that isn’t “recommended” is to use swc. That’s what’s throwing me off.
ive used swc for one of my bots and it didnt have any effect like this
and i dont think it should either since it just compiles the code to js, the env is loaded at runtime
can you log
process.env
somewhere in your code? (after the setup script has run)https://github.com/sapphiredev/examples/tree/main/examples/with-swc also i dont think using swc is that not recommended since there is an official example with it
GitHub
examples/examples/with-swc at main · sapphiredev/examples
Various examples of setting up your bot with the Sapphire Framework - sapphiredev/examples
Here's what it spat out
Doesnt look like it loaded the DISCORD_TOKEN, or any of the other .env values.
hmm.
in that case, seems like the path may not be correct
also is your setup script definitely being called?
in my bots at the top of the main file i always have
import '#lib/setup';
be firstMy
index.ts
looks like this: okay so you do have that imported. id check to make sure your path for the env file is correct then
Only thing I have changed so far here are the intents inserting
process.env.DISCORD_TOKEN
in the login() function.It should be correct. This is all from the template the CLI spun up.
Here's my
dist
directory though. As you can see, the .env file is missing from this directory. But, you did say that shouldn't be the problem right?i am assuming it is to do with the path, since youre nested an extra level
can you get your code to compile down into
dist/
instead of dist/src/
(or add an extra level back to the path youre using)?
the default cli projects all compile down to dist/
and are setup for that nest levelwhich path are you referring to? in the tsconfig?
the path for the env file
Woah, you have to put the env file in the root folder not src
the default templates all have it in
src/
the location of it doesnt matter too much as long as the path name is correctI.e. besides the package.json
Oh? well, the template put it in src. lol. Let me move it
Try it once
chances are moving it would work too since its moving it back a level
Either it's a mistake or the build step is not copying the env file in dist
Or my knowledge is outdated
the env file shouldnt be copied
this is a project setup with the sapphire cli, its by default inside of
src/
Well, how is the compiled code going to read from
src/.env.local
?the way its handled in mine
Fair point
It's still saying an invalid token was provided.
and with the sapphire swc template this is handled in the same level
You may have to modify your setup.ts file
OR
If you are using node v20:
After the build step, use this:
node --env-file <env file path> ./dist/index.js
Yea in this case the code in .dist will travel back to root then to src, thus correctly reading the .env file
yeah
but in their code its nested an extra level inside of
dist/
Alright. Let try. Actually, I'm gonna try to updating Node to see if that helps.
Solution
which is why i was saying to try
export const rootDir = join(__dirname, '..', '..', '..');
so a third level back
or just have it all compile directly to
dist/
Well, here is my setup file.
For you guys' reference.
Its just the default that came with template.
And here's
lib/constants
may you try this please?
if that doesnt work then i have no idea what else to try tbh, but it definitely feels like a pathing issue
Add an extra
'..'
in the rootDir = join() call?
alright.
Gotta wait for brew to finish updating node first though.In the older versions of the sapphire template, build output was directly in
./dist/<compiled source code>
I'm wondering why there's ./dist/src/<compiled source code>
its still the same in the current versions
wait no, for the swc version it does go to
dist/src/
i just missed that cos of the way vscode does this
i wonder why it does that
all the others go straight to
dist/
and i think that's the issuetsconfig file looks right though.
I don't have
baseUrl
property 🤔This came with the template. So, idk. Let me try removing it to see if that helps.
after doing this on a freshly setup swc template
it works fine
but before that it doesnt
so im going to assume that'll solve it for you
unsure why swc puts stuff into
dist/src/
though
its not the tsconfig it seems since i switched it out for the custom one i use in all my bots and its the same outputOkay, that fixed it.
Swc's build options may need a change
Apart from that if nothing works, Node v20+ itself got a nice feature built in.
https://nodejs.org/en/learn/command-line/how-to-read-environment-variables-from-nodejs
This should work in any case
node --env-file <env file path> index.js
(Unless you are building a docker image, then there's different way to provide variables)Node.js — How to read environment variables from Node.js
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
So, I guess the rootDir in
lib/constants.ts
just needed to point up one level.glad to hear it fixed it, sorry it took so long. seems like a bug with the layout of some stuff in the swc template
Thanks for the help. Not a problem at all. Just glad I can get off using Akairo for my bots. lol
On a side note, can you see what is the output directory structure of this command:
npx swc src -d disttest
still the same
that was straight after running it
npx swc src -d disttest --strip-leading-paths
@swc/cli – SWC
SWC is an extensible Rust-based platform for the next generation of fast developer tools. It's used by tools like Next.js, Parcel, and Deno, as well as companies like Vercel, ByteDance, Tencent, Shopify, and more.
ah that does it
probably an easier fix for what i did in a pull request
which was this
This should do it
Because I have seen people using ts-node to test the bot.
And that setup.ts file with triple
..
will send the env file searching outside the source code's directoryi keep forgetting docs exist ;-;
instead i was playing around manually with swcs config
I.e.
rootDir
will be the parent folder of source code if ts-node
is usedi cant even get ts-node to work with it in the first place
lol
Try tsc-watch 🤔
TL;DR: Do not use
ts-node
, use tsc-watch
instead.
We very strongly discourage using ts-node
because it was never meant to be used for bots.
ts-node
is designed for REPL
purposes. That's short for Read Eval Print Loop
.
Which means to read some code, dump it in an eval()
statement, print the result, and loop.
A discord bot is not that.
A Discord bot sets up a permanent web socket connection to the Discord server and connects to the rest gateway.
There is read yes, but no eval, no print, and no loop.
So what should you use instead?
The most ideal way is to just use the watch
flag of tsc
(tsc --watch
) and run node dist/index.js
to run your bot, then cancel that process and restart it when you have changes that require restarting.
You would open 2 terminal tabs, 1 in which you run tsc --watch
and another in which you run the bot.
This is, in particular, the most ideal way, because Discord has a limit to the amount of times you can log in with your bot, or register commands, per day.
Constantly logging in over and over again due to an auto-restarting process will get you close to that limit very quickly and once you exceed it, your development will be halted entirely for the current day.
However, this can be quite tedious so a great package to use instead is tsc-watch
.I mean, that would take us back to using regular tsc instesd of swc like the OP was using
Oh right 🤔
Try this actually
https://github.com/swc-project/swc-node
GitHub
GitHub - swc-project/swc-node: Faster ts-node without typecheck
Faster ts-node without typecheck. Contribute to swc-project/swc-node development by creating an account on GitHub.
then yeah that does present us with the issue you mentioned
I don't personally see that as a big issue since using stuff like that for long running things is bad practice anyways, but regardless your fix is better I think
@KaydaFox @MRDGH2821 Okay. One other small issue with the swc template I should probably let you guys know about.
So, after I fixed the .env problem, like mentioned, the bot was running. But it wasn’t registering any commands.
To fix this, I needed to edit the build script in
package.json
to swc src -d dist —strip-leading-paths
, as mentioned by @MRDGH2821
That’s all from me. Don’t really use ts-node. And, frankly don’t have the time right now to tinker with it. Everything (as in everything included in the template) seems to be working for me as of testing today. I’ll open another thread if I have other issues as I start building out this bot. 🙂Nice!
cool, glad you got it all working in the end ^^
Thanks… don’t think that swc template was very well tested lol.
I hadn't used the template before until last night. Last time I used swc I just replaced tscs build with it manually lol
Unsure how used that template is
First time using it too (and first time using Sapphire). It definitely has its quirks. Even after the changes I made, it still doesn’t like my start script pointing to
dist/index.js
(can’t find env vars again… but it can load commands now). So, it still needs to point to dist/src/index.js
.
Might just file a bug report later today tbh. Can’t be the only one facing this.About this option tho, I see this as a new change.
It wasn't there when I was using it back in 2022
I was using:
"@swc/core": "^1.3.14",
"@swc/helpers": "^0.4.12",
remove the thing i asked you to do for the
rootDir
so replace it with what it was originally, since we altered it to account for the directory nesting, but since --strip-leading-paths
alters that back to what its expected to be, and then envVar should load again
last i used SWC was mid to late 2023, although i also didnt have this issue back thenOkay. So, remove the extra
”..”
? Kk. Will try later. Working on something else rn.Yeah ^^
Just tried it. Looks like it worked. So, I guess the solution here is to change the build script to add the
—strip-leading-paths
flag.yeah, they did that in this commit ^^
https://github.com/sapphiredev/examples/commit/81ff2a4cb69a61046eaff1ec33b2b443049c4eec
Oh cool. So I don’t need to submit a bug report 🙂
It wasn't. Most of use either either tsup (nearly just as fast tbh) or tsc. tsup has the advantage of using esbuild in the background which is a compiler that's much more popular and has much bigger community behind it.