T
TanStack4w ago
exotic-emerald

Server functions with state

is there a way to do something like this but have it work and have the client side call these as server functions?
export function createAuthServerFns(client: Client, auth: Promise<Auth>, options: TanStackAuthOptions) {
const authMiddleware = createAuthMiddleware(client, auth, options);

const withSession = createMiddleware({ type: "function" })
.middleware([authMiddleware])
.server(({ next, context }) => {
const { __gel_auth: auth } = context;
const authCookie = getCookie(auth.options.authCookieName) || getCookie(DEFAULT_AUTH_COOKIE);

return next({ context: { ...context, session: { client: auth.client, authCookie } } });
});

const startOAuth = createServerFn()
.middleware([authMiddleware])
.inputValidator(({ providerName }: { providerName: BuiltinOAuthProviderNames }) => {
...
})
.handler(async ({ data: { providerName }, context: { __gel_auth: auth } }) => {
const pkceSession = await auth.core.then((core: Auth) => core.createPKCESession());
createVerifierCookie(auth, pkceSession.verifier);
const url = getUrl(auth);
return Response.redirect(pkceSession.getOAuthUrl(providerName, url, `${url}?isSignUp=true`));
});

const handleCallback = createFileRoute('/_auth/login/complete')({
params: {parse: (data: Record<"error" | "code" | "error_description" | "provider" | "isSignUp", string | undefined>) => {
...
}
},
server: {
middleware: [authMiddleware],
handler: {
ANY: (async ({ data: { provider, code, isSignUp }, context: { __gel_auth: auth } }) => {
...
}
}
)

const handleSignout = createServerFn()
.middleware([authMiddleware])
.handler(async ({ context: { __gel_auth: auth } }) => {
clearAuthCookie(auth);
return { success: true };
});

return {
authMiddleware,
withSession,
getProvidersInfo,
startOAuth,
handleCallback,
handleSignout,
};
}
export function createAuthServerFns(client: Client, auth: Promise<Auth>, options: TanStackAuthOptions) {
const authMiddleware = createAuthMiddleware(client, auth, options);

const withSession = createMiddleware({ type: "function" })
.middleware([authMiddleware])
.server(({ next, context }) => {
const { __gel_auth: auth } = context;
const authCookie = getCookie(auth.options.authCookieName) || getCookie(DEFAULT_AUTH_COOKIE);

return next({ context: { ...context, session: { client: auth.client, authCookie } } });
});

const startOAuth = createServerFn()
.middleware([authMiddleware])
.inputValidator(({ providerName }: { providerName: BuiltinOAuthProviderNames }) => {
...
})
.handler(async ({ data: { providerName }, context: { __gel_auth: auth } }) => {
const pkceSession = await auth.core.then((core: Auth) => core.createPKCESession());
createVerifierCookie(auth, pkceSession.verifier);
const url = getUrl(auth);
return Response.redirect(pkceSession.getOAuthUrl(providerName, url, `${url}?isSignUp=true`));
});

const handleCallback = createFileRoute('/_auth/login/complete')({
params: {parse: (data: Record<"error" | "code" | "error_description" | "provider" | "isSignUp", string | undefined>) => {
...
}
},
server: {
middleware: [authMiddleware],
handler: {
ANY: (async ({ data: { provider, code, isSignUp }, context: { __gel_auth: auth } }) => {
...
}
}
)

const handleSignout = createServerFn()
.middleware([authMiddleware])
.handler(async ({ context: { __gel_auth: auth } }) => {
clearAuthCookie(auth);
return { success: true };
});

return {
authMiddleware,
withSession,
getProvidersInfo,
startOAuth,
handleCallback,
handleSignout,
};
}
16 Replies
exotic-emerald
exotic-emeraldOP4w ago
import { BuiltinOAuthProviderNames } from "@gel/auth-core/consts";
import { createTanStackAuth } from "@gel/auth-start-react/src";
import { createServerFn } from "@tanstack/react-start";
import { createClient } from "gel";

export const client = createClient();

export const auth = createTanStackAuth(client, { baseUrl: "http://localhost:10713" });

export const startOAuth = createServerFn()
.inputValidator(({ providerName }: { providerName: BuiltinOAuthProviderNames }) => {
return { providerName };
})
.handler(async ({ data: { providerName } }) => {
return auth.startOAuth({ data: { providerName } });
});
import { BuiltinOAuthProviderNames } from "@gel/auth-core/consts";
import { createTanStackAuth } from "@gel/auth-start-react/src";
import { createServerFn } from "@tanstack/react-start";
import { createClient } from "gel";

export const client = createClient();

export const auth = createTanStackAuth(client, { baseUrl: "http://localhost:10713" });

export const startOAuth = createServerFn()
.inputValidator(({ providerName }: { providerName: BuiltinOAuthProviderNames }) => {
return { providerName };
})
.handler(async ({ data: { providerName } }) => {
return auth.startOAuth({ data: { providerName } });
});
being cheeky and wraping it again doesnt work either
exotic-emerald
exotic-emeraldOP4w ago
fwiw im trying to make an auth library which wraps this https://docs.geldata.com/reference/auth
afraid-scarlet
afraid-scarlet4w ago
const handleCallback = createFileRoute
const handleCallback = createFileRoute
this is not supported, you must export as Route. can you reduce this to a minimal reproducer without all the auth stuff? just the core mechanic you want to use
exotic-emerald
exotic-emeraldOP4w ago
https://github.com/Gobot1234/example I think this should be everything with the counter and _counter in lib
GitHub
GitHub - Gobot1234/example
Contribute to Gobot1234/example development by creating an account on GitHub.
exotic-emerald
exotic-emeraldOP3w ago
i think i might be doing something slightly wrong with the queries but it should still get the point across @Manuel Schiller any ideas about this?
afraid-scarlet
afraid-scarlet3w ago
that's not possible how would that work across users even? should all share the same count?
exotic-emerald
exotic-emeraldOP3w ago
Yeah How would you advise doing some form of global state here if I'm in a library if returning server functions doesn't work?
afraid-scarlet
afraid-scarlet3w ago
would calling a function from the server function to get that global state work? a bit more details would help
exotic-emerald
exotic-emeraldOP3w ago
I think so, I kinda just want to share a db connection and a special Auth object for all users (they all get a different client)
afraid-scarlet
afraid-scarlet3w ago
a middleware can be used to provide context for a server function can be anything, so could be the db connection
exotic-emerald
exotic-emeraldOP3w ago
Right but if I wanted to have an API to call startOauthLogin or startEmailAndPass and they don't take any parameters that's not a thing that can be done?
afraid-scarlet
afraid-scarlet3w ago
i dont follow what do you mean by "API to call" here? a server function?
exotic-emerald
exotic-emeraldOP3w ago
Yeah
afraid-scarlet
afraid-scarlet3w ago
you can install a server function middleware globally or on specific server functions and then that will run prior to the server function and will populate the context that is available in the server function handler
afraid-scarlet
afraid-scarlet3w ago
GitHub
[Feature Request] Opt out of global middleware · TanStack router ...
I have a server function that shouldn't run one of the global middlewares I've registered. It would be great to have the option to opt out of that middleware.
exotic-emerald
exotic-emeraldOP3w ago
Okay I think that should be good so if need to export that middleware and then have people install it and then I can have the server functions get context from there? Thank you for the help :)

Did you find this page helpful?