S
SolidJS6mo ago
florian

Split server code from client code

Hey, I am currently working on a project where I'm writing a good amount of server side logic which uses secrets for different things and database stuff which should stay on the server. With solid-start I'm running into a lot of issues where it seems like vite or vinxi or whatever build tool wants to build my server side code for client side use. For example when building my app it looks like it wants to build node builtins used by packages like mongodb and also my own logic for the browser. It doesn't really make any sense to me as I don't use any of that code inside components. I only use e.g. the mongodb package in a file which is just exporting a few wrapper functions for me to query data easier and those functions are only getting used inside server functions. Seeing this makes me feel also kinda unsure if e.g. my secrets are secure and don't end up in client side code by accident. How can I make sure that this won't happen and how do I fix the build issues? Here is a file from my codebase, this is also the only file where I use vinxi:
import {action, cache, redirect} from "@solidjs/router";
import {validate} from "email-validator";
import * as db from "../../../database.js";
import * as auth from "../../../auth.js";
import {deleteCookie, getCookie} from "vinxi/http";

export const signUp = action(async (formData) => {
"use server";
const email = formData.get("email");
if (email && validate(email)) {
const user = await db.getUserByEmail(email);
if (user) {
return new Error("Email address already in use.");
} else {
const code = auth.randomCode();
// send email code
const verifyState = auth.encryptPayload({email, code, intent: "signup"});
return redirect(`/verify?state=${verifyState}`);
}
} else {
return new Error("Invalid email address.");
}
}, "sign-up");

export const checkSession = cache(async () => {
"use server";
const sessionCookie = getCookie("session");
if (sessionCookie) {
try {
const sessionData = auth.decryptPayload(sessionCookie);
if (sessionData.state === auth.SessionState.Authenticated && sessionData.userId) {
const user = await db.getUserById(sessionData.userId);
if (user) {
return redirect("/app");
}
}
throw new Error();
} catch {
deleteCookie("session");
}
}
}, "check-session");
import {action, cache, redirect} from "@solidjs/router";
import {validate} from "email-validator";
import * as db from "../../../database.js";
import * as auth from "../../../auth.js";
import {deleteCookie, getCookie} from "vinxi/http";

export const signUp = action(async (formData) => {
"use server";
const email = formData.get("email");
if (email && validate(email)) {
const user = await db.getUserByEmail(email);
if (user) {
return new Error("Email address already in use.");
} else {
const code = auth.randomCode();
// send email code
const verifyState = auth.encryptPayload({email, code, intent: "signup"});
return redirect(`/verify?state=${verifyState}`);
}
} else {
return new Error("Invalid email address.");
}
}, "sign-up");

export const checkSession = cache(async () => {
"use server";
const sessionCookie = getCookie("session");
if (sessionCookie) {
try {
const sessionData = auth.decryptPayload(sessionCookie);
if (sessionData.state === auth.SessionState.Authenticated && sessionData.userId) {
const user = await db.getUserById(sessionData.userId);
if (user) {
return redirect("/app");
}
}
throw new Error();
} catch {
deleteCookie("session");
}
}
}, "check-session");
No description
No description
18 Replies
mdynnl
mdynnl6mo ago
put use server at the top of the file that's only meant for server for example
// database.js
'use server'
.....
// database.js
'use server'
.....
import * as db from "./database.js"
export const signUp = action(async (formData) => {
'use server'
// sign up code
})
...
import * as db from "./database.js"
export const signUp = action(async (formData) => {
'use server'
// sign up code
})
...
florian
florianOP6mo ago
can't really do that for files where I put my actions in as I'm experiencing other bugs then unfortunately (see here: https://discord.com/channels/722131463138705510/1249053112925425664/1249390490340692069)
mdynnl
mdynnl6mo ago
yes, but you could have your data apis in separate files and keep only the server logic inside use server files
florian
florianOP6mo ago
its still an issue with vinxi unfortunately
No description
florian
florianOP6mo ago
and imo if I only use certain functions on the server then it shouldn't try to build them for the browser in the first place
mdynnl
mdynnl6mo ago
feel free to make a minimal reproduction or invite me to have a look at what's happening i don't think it's easy to statically analyze everything. that's why use server is necessary in the first place
florian
florianOP6mo ago
I'm doing it like this now: server function logic without being wrapped by action or cache in files specially for server logic and put "use server" at the top of the files, then import and wrap the functions in action or cache inside the route file
No description
florian
florianOP6mo ago
so basically like you said haha the docs should get updated to include advise like this tbh atm its not really clear how to handle stuff like this properly thanks for your help @mdynnl 🙏
mdynnl
mdynnl6mo ago
happy to help. docs could definitely improve. a little tangent here. this kinda defeats the point of colocation even though this approach seems like the least that could go wrong.
action(sigupAction.signup, 'sign-up')
action(sigupAction.signup, 'sign-up')
updated the example
florian
florianOP6mo ago
the problem with only putting "use server" only in the function itself is that the vinxi build issues are coming back then it looks like you can't import any vinxi functions in files like these or the build tool will complain like here and tbh making files that contain logic run on the server "use server" makes me feel more safe of accidentally sharing secrets on the client or stuff like that
florian
florianOP6mo ago
for example importing getCookie from vinxi even tho its not getting used anywhere makes crashes the build process with the error I've shared above
No description
mdynnl
mdynnl6mo ago
for some reason, i can't seem to reproduce it
florian
florianOP6mo ago
I will try to cook up a small example from the solid start basic template
mdynnl
mdynnl6mo ago
No description
florian
florianOP6mo ago
For me this will make the build fail: 1. init basic example with javascript and npm i 2. npm i vinxi 3. add
import {getCookie} from "vinxi/http";
import {getCookie} from "vinxi/http";
to src/routes/about.jsx 4. run npm run build
mdynnl
mdynnl6mo ago
i see seems to be the case with javascript specific configuration i'm using typescript build here that means, .jsx files
florian
florianOP6mo ago
that's pretty weird
mdynnl
mdynnl6mo ago
probably worth creating an issue if there isn't already one
Want results from more Discord servers?
Add your server