DEV Community

Yax
Yax

Posted on

⚡️ YSvelGoK: The Ultimate Full-Stack Starter Kit

A deep dive into YSvelGoK: Combining SvelteKit, Go (Gin), and MongoDB into a dockerized powerhouse.

Why I Paired Svelte with Go

We often find ourselves choosing between Developer Experience (DX) and Raw Performance.

  • Node.js/Next.js: Amazing DX, huge ecosystem, but can get heavy.
  • Go: Incredible performance, tiny implementation, but authenticating and structuring a full-stack app from scratch takes time.

I wanted the best of both worlds. So I built YSvelGoK (Yaxel's Svelte + Go Kit). Here's how it works under the hood.

The Stack


🔐 The "Soft Session" Authentication Pattern

Authentication is usually the biggest pain point in Go. I didn't want to rely on a third-party service like Auth0 for a boilerplate, but I also wanted more security than a standard stateless JWT.

I implemented a hybrid approach I call Soft Sessions.

How it works:

  1. Login: User logs in, backend verifies Argon2 hash.
  2. Session Record: A simple document is created in MongoDB (_id, user_id, created_at).
  3. Token Issue: A JWT is signed containing the Session ID (not just the User ID).

The Secret Sauce: Middlewares

In my Go middleware, I don't just check the signature. I also verify the session is alive in MongoDB.

// api/Routes/middleware.go

func RequireSessionValidate() gin.HandlerFunc {
    return func(c *gin.Context) {
        claims := GetCheckedClaims(c)

        // 1. Signature Check
        if claims == nil || claims.Expired() {
             c.AbortWithStatusJSON(401, gin.H{"message": "Unauthorized"})
             return
        }

        // 2. Database "Liveness" Check
        // If the session was deleted from DB (logout), this fails
        // even if the JWT is still mathematically valid.
        user, session, err := Db.ValidateJWTSessionFromClaims(claims, false)
        if err != nil {
            c.AbortWithStatusJSON(401, gin.H{"message": "Session Revoked"})
            return
        }

        c.Set("user", user)
        c.Set("session", session)
        c.Next()
    }
}
Enter fullscreen mode Exit fullscreen mode

This gives us instant revocation (like sessions) with the structured claims of JWTs. MongoDB handles the cleanup automatically via a TTL Index on the sessions collection.


🐳 Dockerizing the Chaos

Orchestrating a frontend, backend, and database manually is annoying. I used Docker Compose to bundle it all.

The coolest part? Using depends_on with healthcheck to ensure the API never crashes because the Database wasn't ready yet.

  database:
    image: mongo:latest
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

  api:
    build: ./api
    depends_on:
      database:
        condition: service_healthy
Enter fullscreen mode Exit fullscreen mode

🏎️ SvelteKit on the Frontend

The frontend uses SvelteKit, but configured to work seamlessly with an external Go backend.

I use a Server Hook (hooks.server.js) to parse the JWT from cookies before the page even renders. This allows the SSR (Server Side Rendering) to know if a user is logged in immediately.

// frontend/src/hooks.server.js
export const handle = async ({ event, resolve }) => {
    const token = event.cookies.get("token");
    if (token) {
        // Optimistically verify signature for UI state
        // Actual data fetching will still be verified by Go
        const { payload } = await jwtVerify(token, SECRET);
        event.locals.user = payload;
    }
    return await resolve(event);
};
Enter fullscreen mode Exit fullscreen mode

Wrapping Up

This architecture has become my go-to for starting new projects. It's type-safe, compiles fast, and the frontend feels incredible.

The code is open source. Feel free to clone it, break it, and fix it!

yxl-prz/YSvelGoK

Top comments (0)