What is the Drizzle convention for storing the `db` object?
In the "Quick Start" guide, it showcases connecting to the database at the root of a module.
However, in real code, this would likely be in a file called "db.ts", and it would not be at the root of the module, since the connection string would not yet be available.
This is my naïve attempt to show what a more real-life code would look like:
And then, elsewhere in the code:
But here, I export a "let" variable, which flags ESLint and suggests that I am architecting my code incorrectly.
Is there a more conventional way to handle the "db" object?
30 Replies
no idea if it helps, but in my AWS Lambda functions, I simply do this, after my imports
then underneath I have a single class, with my various methods in it, which can all use
db
(we have a micro service style setup, where we have 1 lambda function per API endpoint)I had the same problem, but I think I have a bit of a more elegant solution (Tho I did actually do exactly what @mr_pablo_85 suggested for a while)
My current project has
controllers
for each table (UserController, MessageController), so I did this:
I have an abstract class controller
that looks like this:
And then whenever I need a new controller I extend the abstract class and go from there;
Example use:
What is eslint saying exactly? You can just ignore it you know...
What is eslint saying exactly? You can just ignore it you know...To be clear, I'm manually suppressing the error. But usually, when you are manually suppressing ESLint errors, it means you are doing something wrong. Not always, but as a general rule of thumb. You just connect at the top level / root level of the module? That won't work for me because 1) it is importing for side effects, which is generally considered to be nasty 2) settings such as RDS_HOSTNAME and RDS_USERNAME are coming from environment variables and are not yet ready to be used when the module is imported and the top-level code is first executed, which is why it needs to be wrapped inside of an init function.
Re point 2, the env vars should be ready by time the code executes... why wouldn't they be?
Because they come from an .env file, so for example if you are using the
dotenv
library then you have to execute dotenv.config()
earlier on in the execution path.
Merely invoking dotenv.config()
at all in the top-level of any file would have the same problem, you are just importing code for side effects.Just call that first?
Stuff like that should be available from the get go
Or get them from process.env
It shouldn't be called first in the
db.ts
file because other files need environment variables other than just the db.ts
file, which should only handle the database.
We don't want to call it more than one time.I'd say look at this env var issue really. Use process.env so they are available right away. You should know the db connection details so no reason why you have to "wait" for them
I am using
process.env
. dotenv
loads them from disk.
See: https://github.com/motdotla/dotenv
Pretty sure that using .env files is best-practice.Can you not load them into the actual running env?
Either way I think the issue here is how you are using env vars and initialising the drizzle orm
So, two things:
First, I highly recommend giving https://env.t3.gg/docs/introduction a look, it is excellent
Second, @zamiel did you give my solution a look? It's been in prod for me for about a month with no issues, and the DX is pretty solid
Introduction ⋅ T3 Env
Forgetting environment variables during build can be a hassle and difficult to debug if a bug is caused by a missing environment variable. This package provides a simple way to define environment variables validation for your app.
Nice, I'll take a look at T3 Env.
Yes, I gave your solution a look, but I'm not quite sure I understand.
It just looks like you are using the singleton class pattern to manage the db:
https://refactoring.guru/design-patterns/singleton
But in the context of my question, it would be kind of the same thing, e.g.:
-->
I guess my question is more general than just Drizzle, it would extend to any TypeScript server variable that is initialized on server boot and is intended to be used in many other files.
Like a logger or a database connection or a redis client.
(I'm new to writing TypeScript servers, I have typically used Golang in the past.)
Sort of?
It's a singleton, but every one of my controllers is a separate singleton inheriting a specific parent's connection
So I'm actually never directly interfacing with the connection variable outside of my classes, and the classes themselves inherit it from the abstract class which is only responsible for that one thing.
Type inference is therefore super easy and the errors are quite useful
I came up with this solution tho, so there might be a better pattern available. However, from fiddling around and thinking about it (quite a lot, actually) I haven't come up with any better ideas
Oh, I see now. Thank you.
If you want to see an implementation of it, I believe I have a public repo using it gimme a sec and I'll find it
What does the code look like that instantiates the singleton though?
Is it using the
export let
pattern?https://github.com/yuval59/twitch-stats-server
Here's the repo, you need src/db/controllers
GitHub
GitHub - yuval59/twitch-stats-server
Contribute to yuval59/twitch-stats-server development by creating an account on GitHub.
So, my abstract controller is the one making the connection, then every member inherits it with the db connection
Abstract classes are really neat and useful and not enough people use them in ts sadly
You are just connecting to the database at the top level of the module:
https://github.com/yuval59/twitch-stats-server/blob/main/src/db/controllers/controller.ts
This is importing for side effects, so I can't follow this pattern in my code, since the environment variables are not available until I import them.
In general, importing for side effects is considered to be spaghetti / an anti-pattern.
Because the act of importing could cause runtime errors in your codebase, even if you don't ever run the function or reference the variable.
I don't see what you mean?
I'm using t3-env for my environment, could that be the confusion?
t3-env is checking my environment variables on the build step too, so I know for certain that if the code deployed those variables are in my environment and I can safely import them from my env file
Again, this is the issue you need to fix. Why are you using env vars that aren't there until you import them? It makes no sense. Why aren't the there.from the beginning?
Right now it seems you are stuck on this one track and are reluctant to actually.try what's being recommended to you. Good luck
FYI yuval is not "importing for side effects" as.you keep saying. Read the definition here
Why are you using env vars that aren't there until you import them?the environment variables are on disk. so they don't exist until you call out to the file system and read them. this is an extremely common pattern in nodeJS applications, see https://github.com/motdotla/dotenv to be more precise, side effects are occuring on import, which is an antipattern. sometimes you import purely for side effects, and sometimes modules have a mixture of both side effects and exports. both of these situations are antipatterns. in yuval's code, we have the latter situation
So according to you we can never import anything?
As.for the dotenv approach, you should be loading them in before any other code runs. Simple.
i dont understand the question
It's really not difficult. I think you are just being stubborn now
Follow the examples given
I don't think I am being stubborn for not wanting to use side effects.
In general, they are considered evil.
Just google "why are side effects in programming bad"; there are 132 million results.
I haven't read up on this in a while, but I'm sure there are some nice blogs that explain it in some detail.
It's a bit too much for a single Discord message to explain why clearly.
how are you loading envs then if not importing
node -r dotenv/config your_script.js
https://github.com/motdotla/dotenv#preload
GitHub
GitHub - motdotla/dotenv: Loads environment variables from .env for...
Loads environment variables from .env for nodejs projects. - GitHub - motdotla/dotenv: Loads environment variables from .env for nodejs projects.