{"id":18768761,"url":"https://github.com/get-convex/convex-waitlist","last_synced_at":"2025-04-13T07:31:06.757Z","repository":{"id":208531374,"uuid":"721859145","full_name":"get-convex/convex-waitlist","owner":"get-convex","description":"An easy to adopt waitlist implementation using Convex","archived":false,"fork":false,"pushed_at":"2024-05-21T17:17:19.000Z","size":258,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-05-21T18:34:48.658Z","etag":null,"topics":["fullstack","reactive","realtime","typescript","waitlist"],"latest_commit_sha":null,"homepage":"https://get-convex.github.io/convex-waitlist/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/get-convex.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2023-11-21T23:19:03.000Z","updated_at":"2024-05-21T18:34:49.359Z","dependencies_parsed_at":"2024-05-21T18:45:02.817Z","dependency_job_id":null,"html_url":"https://github.com/get-convex/convex-waitlist","commit_stats":null,"previous_names":["get-convex/convex-waitlist"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fconvex-waitlist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fconvex-waitlist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fconvex-waitlist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/get-convex%2Fconvex-waitlist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/get-convex","download_url":"https://codeload.github.com/get-convex/convex-waitlist/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223573806,"owners_count":17167369,"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":["fullstack","reactive","realtime","typescript","waitlist"],"created_at":"2024-11-07T19:13:56.260Z","updated_at":"2024-11-07T19:13:57.118Z","avatar_url":"https://github.com/get-convex.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Convex Waitlist\n\nAn easy to adopt waitlist implementation using [Convex](https://convex.dev).\nRead the [detailed guide](https://stack.convex.dev/waitlist) to this\nimplementation on Stack.\n\n![Screenshot of user on a waitlist](./screenshot.png \"Waiting in the waitlist\")\n\n## Overview\n\nThis repo includes a full-fledged implementation of a waitlist which can be used\nto protect your app against surges in demand.\n\n- The `Waitlist` component renders a replacement UI when the user must wait\n  before they can use the app, as demonstrated in [`App.tsx`](./src/App.tsx)\n- The waitlist backend keeps track of sessions and the number of active\n  sessions, as defined in\n  [`convex/waitlist/schema.ts`](./convex/waitlist/schema.ts)\n- The waitlist is updated periodically via a cron defined in\n  [`convex/waitlist/crons.ts`](./convex/waitlist/crons.ts)\n- The read endpoints powering the UI and all database queries are defined in\n  [`convex/waitlist/read.ts`](./convex/waitlist/read.ts)\n- The waitlist session creation and refresh logic is in\n  [`convex/waitlist/write.ts`](./convex/waitlist/write.ts)\n\n## Demo\n\nYou can play with the\n[live demo](https://get-convex.github.io/convex-waitlist/). It's been configured\nto allow only 3 users at a time to have access. You can open multiple browser\ntabs to create multiple user sessions.\n\n## Running the App\n\n```\nnpm install\nnpm run dev\n```\n\n## Configuration\n\nThe implementation uses the following environment variables, which can be\nconfigured on the Convex dashboard (run `npx convex dashboard` to open it):\n\n- `ACTIVE_SESSIONS_COUNT_LIMIT` how many active (not-waiting) sessions there can\n  be at the same time. Defaults to 100, but you should really change this based\n  on the number of users your app can handle.\n- `WAITLIST_UPDATE_INTERVAL_SECONDS` how often the waitlist should be updated\n  based on active sessions becoming inactive. Defaults to every 60 seconds.\n- `ACTIVE_SESSION_TIMEOUT_SECONDS` for how long an active session can be\n  inactive before it expires. Defaults to 5 minutes. Make this shorter if your\n  app is highly interactive and you want users to move off of the waitlist\n  faster.\n- `WAITING_SESSION_TIMEOUT_SECONDS` how long after a user waiting user leaves\n  should their session expire. Defaults to 1 minute. Make this longer if you\n  expect users to wait a long time and you want them to retain their position in\n  the line.\n\n## Adding waitlist to an existing app\n\n1. Follow one of the Convex [quickstarts](https://docs.convex.dev/quickstarts)\n   to get Convex set up.\n   1. You don’t need to use Convex for anything else other than the waitlist\n      (_but you really should_).\n2. Copy the `convex/waitlist` folder from this repo into your `convex` folder\n3. Set up crons. Adds the waitlist crons setup to your `convex/crons.ts` file:\n\n   ```jsx\n   import { cronJobs } from \"convex/server\";\n   import { setupWaitlistCrons } from \"./waitlist/crons\";\n\n   const crons = cronJobs();\n\n   setupWaitlistCrons(crons);\n\n   // ...your other crons, if any\n\n   export default crons;\n   ```\n\n4. Set up the schema. Add the waitlist tables to your `convex/schema.ts` file:\n\n   ```jsx\n   import { defineSchema, defineTable } from \"convex/server\";\n   import { waitlistTables } from \"./waitlist/schema\";\n\n   export default defineSchema({\n     ...waitlistTables,\n     // ...your other tables, if any\n   });\n   ```\n\n5. Protect your queries (read endpoints) by checking that the current session is\n   active:\n\n   ```jsx\n   import { v } from \"convex/values\";\n   import { query } from \"./_generated/server\";\n   import { validateSessionIsActive } from \"./waitlist/read\";\n\n   export const someRead = mutation({\n     args: {\n       // ... some arguments\n       // any string will do as the user/session identifier\n       sessionId: v.string(),\n     },\n     handler: async (ctx, args) =\u003e {\n       // Check that the user is not still waiting\n       await validateSessionIsActive(ctx, args.sessionId);\n\n       // ... do whatever you need to do ...\n     },\n   });\n   ```\n\n6. Protect your mutations (write endpoints) by checking that the current session\n   is active, and refresh the `lastActive` timestamp. See the\n   [article](https://stack.convex.dev/wait-a-minute-won-t-you) for more details:\n\n   ```jsx\n   import { v } from \"convex/values\";\n   import { mutation } from \"./_generated/server\";\n   import { validateSessionAndRefreshLastActive } from \"./waitlist/write\";\n\n   export const someWrite = mutation({\n     args: {\n       // ... some arguments\n       // any string will do as the user/session identifier\n       sessionId: v.string(),\n     },\n     handler: async (ctx, args) =\u003e {\n       // Check that the user is not still waiting and\n       // record that the user is actively using the app.\n       await validateSessionAndRefreshLastActive(ctx, args.sessionId);\n\n       // ... do whatever you need to do ...\n     },\n   });\n   ```\n\n7. Implement your UI. If you're using React:\n\n   1. Copy the `src/waitlist` folder from this repo into your\n      src/app/pages/lib/whatever client-side source folder\n   2. Wrap your app in the `Waitlist` component, which takes the following\n      props:\n      - `loading` with what is shown while we’re loading the waitlist status\n        from the server\n      - `whileWaiting` to render the UI when the user is waiting\n      - `sessionId` to identify the current user or session\n\n   example:\n\n   ```jsx\n   import { Waitlist } from \"./waitlist/Waitlist\";\n\n   export function App() {\n     const sessionId = useSessionId();\n     return (\n       \u003cWaitlist\n         loading=\"Loading...\"\n         sessionId={sessionId}\n         whileWaiting={(position, numWaiting) =\u003e (\n           \u003cp\u003e\n             You're on the wailist, thanks for your patience!\n             \u003cbr /\u003e\n             Your position in the line: {position} out of {numWaiting}.\u003cbr /\u003e\n             Thanks for waiting.\n           \u003c/p\u003e\n         )}\n       \u003e\n         \u003cp\u003eYou're no longer waiting! Congratz!\u003c/p\u003e\n       \u003c/Waitlist\u003e\n     );\n   }\n   ```\n\n   If you're not using React check out the implemention in\n   [src/waitlist/Watlist.tsx](./src/waitlist/Waitlist.tsx) for inspiration for\n   your own client implementation.\n\nAnd you're done!\n\n# What is Convex?\n\n[Convex](https://convex.dev) is a hosted backend platform with a built-in\ndatabase that lets you write your\n[database schema](https://docs.convex.dev/database/schemas) and\n[server functions](https://docs.convex.dev/functions) in\n[TypeScript](https://docs.convex.dev/typescript). Server-side database\n[queries](https://docs.convex.dev/functions/query-functions) automatically\n[cache](https://docs.convex.dev/functions/query-functions#caching--reactivity)\nand [subscribe](https://docs.convex.dev/client/react#reactivity) to data,\npowering a\n[realtime `useQuery` hook](https://docs.convex.dev/client/react#fetching-data)\nin our [React client](https://docs.convex.dev/client/react). There are also\n[Python](https://docs.convex.dev/client/python),\n[Rust](https://docs.convex.dev/client/rust),\n[ReactNative](https://docs.convex.dev/client/react-native), and\n[Node](https://docs.convex.dev/client/javascript) clients, as well as a\nstraightforward\n[HTTP API](https://github.com/get-convex/convex-js/blob/main/src/browser/http_client.ts#L40).\n\nThe database support\n[NoSQL-style documents](https://docs.convex.dev/database/document-storage) with\n[relationships](https://docs.convex.dev/database/document-ids) and\n[custom indexes](https://docs.convex.dev/database/indexes/) (including on fields\nin nested objects).\n\nThe [`query`](https://docs.convex.dev/functions/query-functions) and\n[`mutation`](https://docs.convex.dev/functions/mutation-functions) server\nfunctions have transactional, low latency access to the database and leverage\nour [`v8` runtime](https://docs.convex.dev/functions/runtimes) with\n[determinism guardrails](https://docs.convex.dev/functions/runtimes#using-randomness-and-time-in-queries-and-mutations)\nto provide the strongest ACID guarantees on the market: immediate consistency,\nserializable isolation, and automatic conflict resolution via\n[optimistic multi-version concurrency control](https://docs.convex.dev/database/advanced/occ)\n(OCC / MVCC).\n\nThe [`action` server functions](https://docs.convex.dev/functions/actions) have\naccess to external APIs and enable other side-effects and non-determinism in\neither our [optimized `v8` runtime](https://docs.convex.dev/functions/runtimes)\nor a more\n[flexible `node` runtime](https://docs.convex.dev/functions/runtimes#nodejs-runtime).\n\nFunctions can run in the background via\n[scheduling](https://docs.convex.dev/scheduling/scheduled-functions) and\n[cron jobs](https://docs.convex.dev/scheduling/cron-jobs).\n\nDevelopment is cloud-first, with\n[hot reloads for server function](https://docs.convex.dev/cli#run-the-convex-dev-server)\nediting via the [CLI](https://docs.convex.dev/cli). There is a\n[dashbord UI](https://docs.convex.dev/dashboard) to\n[browse and edit data](https://docs.convex.dev/dashboard/deployments/data),\n[edit environment variables](https://docs.convex.dev/production/environment-variables),\n[view logs](https://docs.convex.dev/dashboard/deployments/logs),\n[run server functions](https://docs.convex.dev/dashboard/deployments/functions),\nand more.\n\nThere are built-in features for\n[reactive pagination](https://docs.convex.dev/database/pagination),\n[file storage](https://docs.convex.dev/file-storage),\n[reactive search](https://docs.convex.dev/text-search),\n[https endpoints](https://docs.convex.dev/functions/http-actions) (for\nwebhooks),\n[streaming import/export](https://docs.convex.dev/database/import-export/), and\n[runtime data validation](https://docs.convex.dev/database/schemas#validators)\nfor [function arguments](https://docs.convex.dev/functions/args-validation) and\n[database data](https://docs.convex.dev/database/schemas#schema-validation).\n\nEverything scales automatically, and it’s\n[free to start](https://www.convex.dev/plans).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fget-convex%2Fconvex-waitlist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fget-convex%2Fconvex-waitlist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fget-convex%2Fconvex-waitlist/lists"}