#Passing Client Object to "@auth" Package - unable to solve expert advice wanted / code injection?

89 messages · Page 1 of 1 (latest)

dark hare
#

Seeking advice: Passing client object to "@auth" package

I need expert advice on passing a client object to the "@auth" package in my Remix application.

In the login route (app/routes/_auth/login.tsx), I call the loginUser method from the "@auth" package and pass the client object.

However, I also need the client object to be accessible in other parts of the package, such as auth.server.ts and session.server.ts.

I appreciate any advice on ensuring the availability of the client object throughout the "@auth" package. Thank you!

In session.server.ts:

import { createCookieSessionStorage } from "@remix-run/node";
import type { User, UserProfile } from the client here
export interface UserAuthSession extends User {
    // userProfile: UserProfile;
}

I would greatly appreciate any expert advice on ensuring that the client object is accessible throughout the "@auth" package code. Thank you!

frigid raft
#

im ngl this is still not clear enough

#
import { loginUser } from "@auth/auth";

export async function action({ request }: ActionArgs) {
    const result = await loginUser(client, request);
}

where are you getting client from?

import type { User, UserProfile } from the client here

types have to be statically known anyway; how would you do this?

dark hare
#

Re edit its more clear now you think?

dark hare
#

Its a really hard problem trying to explain since its a bit “complex” does this illustrate the idea? I

#

I want to move the model files to a package and use the client that i define in the app then move the models to a package and “pass the app defined client to the package”

frigid raft
#

i mean, cant you just

const client = whateverClient();

export type User = typeof client["User"];
#

or smth

frigid raft
dark hare
#

Yes that i do in my app the problem is having access to the client in the mono repro package

frigid raft
#

whats the problem

#

obviously this is a side effect but other than that i don't see how its not possible

dark hare
#

If i define client in app the do a loginUser method in package auth i can import that method in my app and pass client as argument loginuser(client, bla) but i onlu have client in the method available not in the whole package

#

On this for a day 🤣 really hard to explain

frigid raft
#

yeah because you need to import it every time you have to use it?

#

client is part of the module itself (ignoring side effects) i don't see why you can't just import it

dark hare
#

You cannot import inside a package. The db client const is defined in the app not in the package

#

And each app had its own client ( it holds db schema) so you see the problem?

#

One needs to pass the client defined in the app to the package for methods this works

frigid raft
#

so what you are saying is

#

you kinda want an entire package to be initialized to a db schema of each app

dark hare
#
import { loginUser } from "@auth/auth";

export async function action({ request }: ActionArgs) {
    const result = await loginUser(client, request);
}

This works but

`import type { User, UserProfile } from client here does not, no access to client here

frigid raft
#

is client a global variable

dark hare
#

The package implements generic logic for auth and needs access to the dbclient true dbclient it accesses the right database

dark hare
#

Really my typescrips lacks here to better explain it

frigid raft
#

you made a package that requires some set of client to be present, and that client also needs to satisfy some type constraints

dark hare
#

So instead of regularly import client from “app/db.server.ts” as you would do in the app. The package imports from the client that it recieved from the app instead

dark hare
frigid raft
#

well, the absolute best way imo is to

// package/src/action-handler.ts
type User = {} // whatever type you need

export async function initActionHandler({ client }: { client: SomeGeneric<User> }) {
  return async function({ request }) {
    // client is present here
  }
}

and in your application directory

// application/src/handlers/action-handler.ts
import client from "./client-initializer" // or whatever

export const actionHandler = initActionHandler({ client }); // typechecked at this step

alternatively, you can batch initialize

// application/src/handlers.ts
export const actionHandler = initActionHandler({ client });
export const action2handler = initAction2Handler({ client });

// if you don't really care about static analysis, something like
export default { ... } // initialize an array of handlers here

then you can use action handler in other files in your app normally

#

another way is to use DI (dependency injection)

dark hare
#

I investigated di but got a big stuck on a working solution

frigid raft
dark hare
#

I assume with ts one can pass the dbclient object to a method in the package and then use that to “ reuse” it in the package code?

#

One could not use those imports

#

import type { Note } from "~/db.server";
import { e, client } from "~/db.server";

#

Normally one imports from there the client and types

#

So i need a way to retrieve the client somehow like magically hand if over from app to package and be able to do import client from “passed from app”

frigid raft
#

if you are using DI, however

#
import container from "some-di-package";

export async function action({ request }: ActionArgs) {
    const result = await loginUser(container.client, request);
}
#

the idea is that container.client gets written before action is called

dark hare
#

Ok much appreciated this is the first ts issue i havent been able to solve so far.

#

Must admit i have abit of hard time understanding this example

frigid raft
#

and you need to import actionHandler, not initActionHandler

#

essentially you are creating a layer of concrete functions somewhere in your app

#

instead of importing those functions directly from consumer

dark hare
#

Ahh like that now i see🙏 this seems really cool i try to inplement this way and see how it works out much appreciated. Did go well beyound my 6 month ts skills

frigid raft
#

pinia has this pattern btw

dark hare
#

Always been on rails but i think indeed this will seems better inderd

#

Im using this in a remix app with mono repro based on turbo

dark hare
#

Let me try to code this implementation in its 4.38 here now 🤣 have to get some more sleep then i code it in a few hours

frigid raft
#

its just a state management library

dark hare
#

Really cool guess this is a bit more advanched ts

frigid raft
#

but don't you think you are generalizing your packages a little too much

#

because i am also in a similar situation i just concretely typed client and pass in the client as a parameter

dark hare
#

The idea is to have an auth package incan reuse in several apps

frigid raft
#

which is the easiest solution

dark hare
frigid raft
#

well, if you want to avoid DIs and passing as parameter what I described is pretty much the best solution

dark hare
#

Or do i overlook something?

#

A route file localhost/notes loads that file gets the method tonretrieve notes from models file and it uses the client there

frigid raft
#

yeah just wrap it in a function

#

the entire thing

dark hare
#

Would become messy?

frigid raft
#

well, you are trying to generalize

dark hare
#

I like the seperatio ln off the db methods in /models

#

I could not do that if i would create a loginuser method?

#

I could put all code in the method but really messy unmaintainable

frigid raft
#

for example this

#
export function User(id: string) {
  return e.select(e.User, (user) => ({
    filter: e.op(user.id, "=", e.uuid(id)),
  }));
}

would be

export function initUserMethod(e: { User: SomeType } & SomeBaseType) {
  return function (id: string) {
    return e.select(e.User, (user) => ({
      filter: e.op(user.id, "=", e.uuid(id)),
    }));
  }
}
dark hare
#

Really cool db btw edgedb

#

I still dont like inhave to define it that way i wish i could just pass the client to the package and use the same code as in the examples

#

But guess it does not work that way in ts

#

I mean wish i could just do ```import type { Note } from "~/db.server";
import { e, client } from "~/db.server";

export function User(id: string) {
return e.select(e.User, (user) => ({
filter: e.op(user.id, "=", e.uuid(id)),
}));
}
export async function getNote(params: { id: string; userId: string }) {
const userNotes = e.select(e.Note, (note) => ({
filter: e.op(note.user, "=", User(params.userId)),
}));
const note = e
.select(userNotes, (note) => ({
...e.Note["*"],
filter: e.op(note.id, "=", e.uuid(params.id)),
}))
.run(client);
return note;
} ``` but instead of import from db.server.ts retrieve the client else

#

Problem is i need this for multiple packages to be able to pass the db client to packages and have access to my database throughout