{"id":18160713,"url":"https://github.com/blankeos/realtime-user-status","last_synced_at":"2025-04-07T03:54:41.743Z","repository":{"id":245479962,"uuid":"818002240","full_name":"Blankeos/realtime-user-status","owner":"Blankeos","description":"Quick demo on a making realtime user status with timer.","archived":false,"fork":false,"pushed_at":"2024-08-04T18:20:35.000Z","size":878,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-13T08:41:22.315Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Blankeos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-20T22:28:16.000Z","updated_at":"2024-12-05T17:51:53.000Z","dependencies_parsed_at":"2024-06-22T10:18:05.601Z","dependency_job_id":"83013077-a89b-46cc-8b53-80368da81373","html_url":"https://github.com/Blankeos/realtime-user-status","commit_stats":null,"previous_names":["blankeos/realtime-user-status"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blankeos%2Frealtime-user-status","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blankeos%2Frealtime-user-status/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blankeos%2Frealtime-user-status/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blankeos%2Frealtime-user-status/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Blankeos","download_url":"https://codeload.github.com/Blankeos/realtime-user-status/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247589831,"owners_count":20963022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-02T08:09:06.469Z","updated_at":"2025-04-07T03:54:41.721Z","avatar_url":"https://github.com/Blankeos.png","language":"TypeScript","readme":"# Realtime User Status Change\n\n![alt text](docs/preview.png)\n\n**Feature Demonstrations**\n\n- [x] Auth\n- [x] Image change\n- [x] Last Status Updated with Timer\n- [x] Realtime pubsub with websockets\n- [x] HMR using a custom `vite.middlewares` + `connectToWeb`.\n- [x] Watch with Nodemon (bun run --watch is broken because it conflicts with Vite's `createServer`)\n\n**Notable Stack**\n\n- Hono\n- tRPC (subscriptions)\n- Bun (websockets)\n\n**Used**\n\n- Hono and Bun Websocket Upgrading to 101 Switching Protocol: https://hono.dev/docs/helpers/websocket\n- Bun Websocket Handling the tRPC router: https://github.com/cah4a/trpc-bun-adapter\n\n---\n\nBootstrapped with:\n\n## 💙 Solid Launch\n\n\u003e An sophisticated boiler-plate built for **simplicity**.\n\n![Image](https://assets.solidjs.com/banner?type=Starter%20Kit\u0026background=tiles\u0026project=Solid%20Launch)\n\n[Carlo](https://carlo.vercel.app/)'s starter for making a Vike + Solid app with batteries included on stuff I like after experimenting for years.\n\nThis is handcrafted from my own research. This might not work for you, but it works for me. 🤓\n\nAlternatively:\n\n- [🐇 Solid Hop](https://github.com/blankeos/solid-hop) - Less-opinionated Vike Solid boilerplate. Like `npx create solid` but simpler.\n- [🧡 Svelte Launch](https://github.com/blankeos/svelte-launch) - Svelte, but same robust practices.\n\n### Benefits\n\n- [x] 🐭 **Handcrafted and minimal** - picked and chose \"do one thing, do it well\" libraries that are just enough to get the job done. Just looks a bit bloated at a glance. (I kinda made my own NextJS from scatch here)\n- [x] ⚡️ **Super-fast dev server** - way faster than NextJS thanks to Vite. You need to feel it to believe it! It can also literally build your app in seconds.\n- [x] 💨 **Fast, efficient, fine-grained Reactivity** - thanks to Solid, it's possibly the most enjoyable framework I used that uses JSX. Has state management primitives out-of-the-box and keeps the experience a breeze.\n- [x] 🐍 **Extremely customizable** - you're not at the mercy of limited APIs and paradigms set by big frameworks or third-party services. Swap with your preferred JS backend framework/runtime if you want. Vike is just a middleware. Most of the tech I use here are open-source and roll-your-own type of thing. Hack it up! You're a dev aren't you?\n- [x] ☁️ **Selfhost-ready** - Crafted with simple hosting in mind that'll still probably scale to millions. Just spin up Docker container on a good'ol VPS without locking into serverless. DHH and Shayan influenced me on this. You can still host it on serverless tho. I think? lol\n- [x] **🔋 Batteries-included** - took care of the hard stuff for you. A well-thought-out folder structure from years of making projects: a design system, components, utilities, hooks, constants, an adequate backend DDD-inspired sliced architecture that isn't overkill, dockerizing your app, and most importantly---perfectly-crafted those pesky config files.\n\n### Tech Stack\n\n- [x] **Bun** - Runtime and package manager. You can always switch to Node and PNPM if you wish.\n- [x] **SolidJS** - Frontend framework that I like. Pretty underrated, but awesome!\n- [x] **Vike** - Like NextJS, but just a middleware. SSR library on-top of Vite. Use on any JS backend. Flexible, Simple, and Fast!\n- [x] **Hono** - 2nd fastest Bun framework(?), run anywhere, uses easy-to-understand web-standard paradigms.\n- [x] **tRPC** - E2E typesafety without context switching. Just amazing DevX.\n- [x] **Tailwind** - Styling the web has been pretty pleasant with it. I even use it on React Native for work. It's amazing.\n- [x] **Prisma** - Great _migrations_ workflow, but I want to maximize perf.\n- [x] **Kysely** - Great typesafe _query builder_ for SQL, minimally wraps around db connection.\n- [x] **SQLite/LibSQL (Turso)** - Cheapest database, easy to use.\n- [x] **Lucia** - Makes self-rolling auth easy.\n- [ ] **SES or MimePost** - Emails\n- [ ] **Backblaze** - Cheap blob object storage with an S3-compatible API.\n- [ ] **Stripe, Payrex, or Xendit** - Accept payments.\n\n\u003e You can also easily swap the database if you want.\n\u003e\n\u003e - [ ] **Postgres** - powerful relational DB. (TBD)\n\u003e - [ ] **CockroachDB** - serverless database. (TBD)\n\u003e - [ ] **MongoDB** - cheap easy to use database. (TBD)\n\n### QuickStart\n\nI'll assume you don't want to change anything with this setup after cloning so let's get to work!\n\n1. Copy the environment variables\n\n   ```sh\n   cp .env.example .env\n   ```\n\n2. Replace the `\u003cabsolute_url\u003e` in the local database with:\n\n   ```sh\n   pwd # If it outputs: /User/Projects/solid-launch\n\n   # Replace the .env with:\n   DATABASE_URL=\"file:/User/Projects/solid-launch/local.db\"\n   ```\n\n3. Generate\n\n   ```sh\n   bun db:generate # generates Kysely and Prisma client types.\n   bun db:migrate # migrates your database.\n   ```\n\n4. Install deps and run dev\n\n   ```sh\n   bun install\n   bun dev\n   ```\n\n### Useful Development Tips\n\nI took care of the painstaking parts to help you develop easily on a SPA + SSR + backend paradigm. You can take take these practices to different projects as well.\n\n1.  Make use of the `code-snippets` I added. It'll help!\n2.  Check all typescript errors (`Cmd` + `Shift` + `B` \u003e `tsc:watch tsconfig.json`).\n3.  Authentication Practices - I have these out-of-the-box for you so you won't have to build it.\n\n    - Getting Current User\n\n      ```ts\n      import { useAuthContext } from '@/context/auth.context';\n\n      export default function MyComponent() {\n       const { user } = useAuthContext();\n      }\n      ```\n\n    - Login, Logout, Register\n\n      ```tsx\n      import { useAuthContext } from '@/context/auth.context';\n\n      export default function MyComponent() {\n      const { login, logout, register } = useAuthContext();\n      }\n      ```\n\n    - Hydrating Current User\n\n      This will also automatically hydrate in your layouts. Anywhere you use `useAuthStore()`, it's magic. (Thanks to Vike's `useData()`. Fun fact: You actually can't do this in SolidStart because it's architecturally different to Vike).\n\n      ```tsx\n      // +data.ts\n      import { initTRPCSSRClient } from '@/lib/trpc-ssr-client';\n      import { PageContext } from 'vike/types';\n\n      export type Data = ReturnType\u003cAwaited\u003ctypeof data\u003e\u003e;\n\n      export async function data(pageContext: PageContext) {\n         const { request, response } = pageContext;\n\n         const trpcClient = initTRPCSSRClient(request.header(), response.headers); // Pass the headers here.\n\n         const result = await trpcClient.currentUser.query();\n\n         return {\n            user: result.user ?? null,\n         };\n      }\n      ```\n\n    - Protecting Routes (Client-Side)\n\n      ```tsx\n      import ProtectedRoute from '@/components/common/protected-route';\n\n      export default MyComponent() {\n         return (\n            \u003cProtectedRoute\u003e\n              On the server (hydration), this part will not be rendered if unauthenticated.\n\n              On the client, you will be redirected to a public route if unauthenticated.\n            \u003c/ProtectedRoute\u003e\n         )\n      }\n      ```\n\n    - Protecting Routes (SSR)\n\n      ```ts\n      // +guard.ts (If you don't have +data.ts in the same route).\n      export async function guard(pageContext: PageContext) {\n         const { request, response } = pageContext;\n\n         const trpcClient = initTRPCSSRClient(request.header(), response.headers); // Pass the headers here.\n\n         const result = await trpcClient.currentUser.query();\n\n         if (!result.user) {\n            throw redirect(\"/\") // Must be a public route.\n         }\n      }\n\n      // +guard.ts (If you already have a +data.ts that gets the user).\n      // ⚠️ I have not tested this. This depends on `+guard` being called after `+data` is resolved.\n      export async function guard(pageContext: PageContext) {\n\n         if (!pageContext.data?.user) {\n            throw redirect(\"/\"); // Must be a public route.\n         }\n      }\n      ```\n\n4.  Dataloading Practices - Also have these out-of-the-box for most usecases since they're tricky to do if you're clueless:\n    - Tanstack Query (Client-only) - Use `trpc-client.ts`\n    - Hydrated Tanstack Query (SSR) - Use `create-dehydrated-state.ts` + `trpc-ssr-client.ts`\n\n### Backend Architecture\n\nMy backend architecture is inspired by DDD where I separate in layers, but I keep it pragmatic by not going too overkill with Entities, Domains, and Aggregates. I personally still like the anemic data-driven architecture for most of my apps since the\napps I make aren't too business-logic-heavy.\n\n```sh\n.\n└── server/ # - root\n    ├── dao/ # - data-access-objects\n    │   └── *.dao.ts\n    ├── modules/\n    │   └── \u003cmodule\u003e/\n    │       ├── services/\n    │       │   └── *.service.ts # 1 service usecase\n    │       └── \u003cmodule\u003e.controller.ts\n    └── _app.ts # - root TRPC router.\n```\n\n- **`dao`** - abstracted away all queries here to interface with them as plain functions. It actually helps me mentally collocate db queries from service logic when I'm using them inside the service.\n- **`modules`** - a vertical slice of each module-group. This just depends on how I feel about grouping them. You get better overtime.\n- **`\u003cmodule\u003e.controller.ts`** - pretty much a group of http endpoints. I can put the DTOs/Validations for each endpoint here without context-switching.\n- **`services`** - these are even smaller pieces of logic that can be used inside each endpoint. It's not necessary to use if the app isn't too big, but it helps.\n- **`_app.ts`** - The root trpc router where the `AppRouter` type is exported.\n\n### Deployment\n\n\u003e [!WARNING]\n\u003e\n\u003e Still in progress\n\nHere are some guides on how to deploy.\n\n- [ ] Kamal (self-host VPS - I recommend)\n- [ ] Dokku (self-host VPS)\n- [ ] Caprover (self-host VPS)\n- [ ] Cloudflare (serverless + static)\n- [ ] Vercel (serverless + static)\n- [ ] Netlify (static)\n\n### Future Plans\n\n\u003e I'll probably make a swapping guide soon. To replace to these:\n\u003e\n\u003e - Runtime: Bun -\u003e Node\n\u003e - Package Manager: Bun -\u003e PNPM\n\u003e - ORM: Prisma -\u003e Drizzle\n\u003e - Database: SQLite -\u003e PostgreSQL, CockroachDB, MongoDB\n\n\u003c!-- ## Usage\n\n```bash\n$ npm install # or pnpm install or yarn install\n```\n\n### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)\n\n## Available Scripts\n\nIn the project directory, you can run:\n\n### `npm run dev`\n\nRuns the app in the development mode.\u003cbr\u003e\nOpen [http://localhost:5173](http://localhost:5173) to view it in the browser.\n\n### `npm run build`\n\nBuilds the app for production to the `dist` folder.\u003cbr\u003e\nIt correctly bundles Solid in production mode and optimizes the build for the best performance.\n\nThe build is minified and the filenames include the hashes.\u003cbr\u003e\nYour app is ready to be deployed!\n\n## Deployment\n\nLearn more about deploying your application with the [documentations](https://vitejs.dev/guide/static-deploy.html) --\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblankeos%2Frealtime-user-status","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblankeos%2Frealtime-user-status","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblankeos%2Frealtime-user-status/lists"}