{"id":13581138,"url":"https://github.com/denoland/saaskit","last_synced_at":"2025-05-14T13:08:14.554Z","repository":{"id":144854296,"uuid":"611568730","full_name":"denoland/saaskit","owner":"denoland","description":"A modern SaaS template built on Fresh.","archived":false,"fork":false,"pushed_at":"2025-04-23T07:59:37.000Z","size":1932,"stargazers_count":1290,"open_issues_count":13,"forks_count":163,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-04-23T08:48:38.530Z","etag":null,"topics":["deno","fresh","saas","saas-boilerplate","typescript"],"latest_commit_sha":null,"homepage":"https://deno.com/saaskit","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/denoland.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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,"zenodo":null}},"created_at":"2023-03-09T04:47:57.000Z","updated_at":"2025-04-23T07:59:42.000Z","dependencies_parsed_at":"2024-01-02T02:44:10.291Z","dependency_job_id":"f35d4a32-a52a-42cd-90a0-e5177e8d4b0b","html_url":"https://github.com/denoland/saaskit","commit_stats":{"total_commits":666,"total_committers":43,"mean_commits":"15.488372093023257","dds":"0.30330330330330335","last_synced_commit":"442871e504890345d7685df96a75c24cf3ab70f5"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denoland%2Fsaaskit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denoland%2Fsaaskit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denoland%2Fsaaskit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denoland%2Fsaaskit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/denoland","download_url":"https://codeload.github.com/denoland/saaskit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254149960,"owners_count":22022851,"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":["deno","fresh","saas","saas-boilerplate","typescript"],"created_at":"2024-08-01T15:01:58.456Z","updated_at":"2025-05-14T13:08:09.537Z","avatar_url":"https://github.com/denoland.png","language":"TypeScript","readme":"# Deno SaaSKit\n\n[![Discord Chat](https://img.shields.io/discord/684898665143206084?logo=discord\u0026style=social)](https://discord.gg/deno)\n[![CI](https://github.com/denoland/saaskit/actions/workflows/ci.yml/badge.svg)](https://github.com/denoland/saaskit/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/denoland/saaskit/branch/main/graph/badge.svg?token=77F8TYTP13)](https://codecov.io/gh/denoland/saaskit)\n[![Built with the Deno Standard Library](https://raw.githubusercontent.com/denoland/deno_std/main/badge.svg)](https://deno.land/std)\n\n[Deno SaaSKit](https://deno.com/saaskit) is an open-sourced, highly performant\ntemplate for building your SaaS quickly and easily.\n\n\u003e Note: this project is in beta. Design, workflows, and user accounts are\n\u003e subject to change.\n\n## Features\n\n- Deno's built-in [formatter](https://deno.land/manual/tools/formatter),\n  [linter](https://deno.land/manual/tools/linter) and\n  [test runner](https://deno.land/manual/basics/testing) and TypeScript support\n- Next-gen web framework with [Fresh](https://fresh.deno.dev/)\n- In-built data persistence with [Deno KV](https://deno.com/kv)\n- High-level OAuth with [Deno KV OAuth](https://deno.land/x/deno_kv_oauth)\n- Modern CSS framework with [Tailwind CSS](https://tailwindcss.com/)\n- Responsive, SaaS-oriented design\n- Dashboard with users view and statistics chart\n- Stripe integration (optional)\n- First-class web performance\n- [REST API](#rest-api-reference)\n- Blog with RSS feed and social sharing icons\n- HTTP security headers\n\n## Get Started\n\n### Get Started Locally\n\nBefore starting, you'll need:\n\n- A GitHub account\n- The [Deno CLI](https://deno.com/manual/getting_started/installation) and\n  [Git](https://github.com/git-guides/install-git) installed on your machine\n\nTo get started:\n\n1. Clone this repo:\n   ```bash\n   git clone https://github.com/denoland/saaskit.git\n   cd saaskit\n   ```\n1. Create a new `.env` file.\n1. Navigate to GitHub's\n   [**New OAuth Application** page](https://github.com/settings/applications/new).\n1. Set **Application name** to your desired application name. E.g. `ACME, Inc`.\n1. Set **Homepage URL** to `http://localhost:8000`.\n1. Set **Authorization callback URL** to `http://localhost:8000/callback`.\n1. Click **Register application**.\n1. Copy the **Client ID** value to the `.env` file:\n   ```bash\n   GITHUB_CLIENT_ID=\u003cGitHub OAuth application client ID\u003e\n   ```\n1. On the same web page, click **Generate a new client secret**.\n1. Copy the **Client secret** value to the `.env` file on a new line:\n   ```bash\n   GITHUB_CLIENT_SECRET=\u003cGitHub OAuth application client secret\u003e\n   ```\n1. Start the server:\n   ```bash\n   deno task start\n   ```\n1. Navigate to `http://localhost:8000` to start playing with your new SaaS app.\n\n### Set Up Stripe (Optional)\n\nThis guide will enable test Stripe payments, the pricing page, and \"Premium\nuser\" functionality.\n\nBefore starting, you'll need:\n\n- A [Stripe](https://stripe.com) account\n- The [Stripe CLI](https://stripe.com/docs/stripe-cli#install) installed and\n  signed-in on your machine\n\nTo get started:\n\n1. Navigate to the\n   [**API keys** page](https://dashboard.stripe.com/test/apikeys) on the\n   **Developers** dashboard.\n1. In the **Standard keys** section, click **Reveal test key** on the **Secret\n   key** table row.\n1. Click to copy the value and paste to the `.env` file:\n   ```bash\n   STRIPE_SECRET_KEY=\u003cStripe secret key\u003e\n   ```\n1. Run the Stripe initialization script:\n   ```bash\n   deno task init:stripe\n   ```\n1. Copy the Stripe \"Premium Plan\" price ID to the `.env` file:\n   ```bash\n   STRIPE_PREMIUM_PLAN_PRICE_ID=\u003cStripe \"Premium Plan\" price ID\u003e\n   ```\n1. Begin\n   [listening locally to Stripe events](https://stripe.com/docs/cli/listen):\n   ```bash\n   stripe listen --forward-to localhost:8000/api/stripe-webhooks --events=customer.subscription.created,customer.subscription.deleted\n   ```\n1. Copy the **webhook signing secret** to the `.env` file:\n   ```bash\n   STRIPE_WEBHOOK_SECRET=\u003cStripe webhook signing secret\u003e\n   ```\n1. Start the server:\n   ```bash\n   deno task start\n   ```\n1. Navigate to `http://localhost:8000` to start playing with your new SaaS app\n   with Stripe enabled.\n\n\u003e Note: You can use\n\u003e [Stripe's test credit cards](https://stripe.com/docs/testing) to make test\n\u003e payments while in Stripe's test mode.\n\n### Bootstrap the Database (Optional)\n\nUse the following commands to work with your local Deno KV database:\n\n- `deno task db:seed` - Populate the database with data from the\n  [Hacker News API](https://github.com/HackerNews/API).\n- `deno task db:dump \u003e backup.json` - Write all database entries to\n  `backup.json`.\n- `deno task db:restore backup.json` - Restore the database from `backup.json`.\n- `deno task db:reset` - Reset the database. This is not recoverable.\n\n## Customize and Extend\n\n### Global Constants\n\nThe [utils/constants.ts](utils/constants.ts) file includes global values used\nacross various aspects of the codebase. Update these values according to your\nneeds.\n\n### Create a Blog Post\n\n1. Create a `.md` file in the [/posts](/posts) with the filename as the slug of\n   the blog post URL. E.g. a file with path `/posts/hello-there.md` will have\n   path `/blog/hello-there`.\n1. Write the\n   [Front Matter](https://daily-dev-tips.com/posts/what-exactly-is-frontmatter/)\n   then [Markdown](https://www.markdownguide.org/cheat-sheet/) text to define\n   the properties and content of the blog post.\n\n   ````md\n   ---\n   title: This is my first blog post!\n   publishedAt: 2022-11-04T15:00:00.000Z\n   summary: This is an excerpt of my first blog post.\n   ---\n\n   # Heading 1\n\n   Hello, world!\n\n   ```javascript\n   console.log(\"Hello World\");\n   ```\n   ````\n1. Start the server:\n   ```bash\n   deno task start\n   ```\n1. Navigate to the URL of the newly created blog post. E.g.\n   `http://localhost:8000/blog/hello-there`.\n\nSee other examples of blog post files in [/posts](/posts).\n\n### Themes\n\nYou can customize theme options such as spacing, color, etc. By default, Deno\nSaaSKit comes with `primary` and `secondary` colors predefined within\n`tailwind.config.ts`. Change these values to match your desired color scheme.\n\n### Cover Image\n\nTo replace the cover image, replace the [/static/cover.png](/static/cover.png)\nfile. If you'd like to change the filename, also be sure to change the\n`imageUrl` property in the [`\u003cHead /\u003e`](/components/Head.tsx) component.\n\n## Deploy to Production\n\nThis section assumes that a\n[local development environment](#get-started-locally) is already set up.\n\n1. Navigate to your\n   [GitHub OAuth application settings page](https://github.com/settings/developers).\n1. Set the **Homepage URL** to your production URL. E.g.\n   `https://hunt.deno.land`.\n1. Set the **Authorization callback URL** to your production URL with the\n   `/callback` path. E.g. `https://hunt.deno.land/callback`.\n1. Copy all the environment variables in your `.env` file to your production\n   environment.\n\n### Deploy to [Deno Deploy](https://deno.com/deploy)\n\n1. Clone this repository for your SaaSKit project.\n1. Update your `.github/workflows/deploy.yml` file as needed. Hints are in the\n   file.\n1. Sign into [Deno Deploy](https://dash.deno.com/projects) with your GitHub\n   account.\n1. Click **+ New Project**.\n1. Select your GitHub organization or user, repository, and branch.\n1. Click **Edit mode** and select **Build step with GitHub Actions** as the\n   build mode and `main.ts` as the entry point.\n1. Click **Add Build Step** and wait until the GitHub Actions Workflow is\n   complete.\n1. Once the deployment is complete, click on **Settings** and add the production\n   environmental variables, then hit **Save**.\n\nYou should now be able to visit your newly deployed SaaS website.\n\n### Deploy to any VPS with Docker\n\n[Docker](https://docker.com) makes it easy to deploy and run your Deno app to\nany virtual private server (VPS). This section will show you how to do that with\nAWS Lightsail and Digital Ocean.\n\n1. [Install Docker](https://docker.com) on your machine, which should also\n   install\n   [the `docker` CLI](https://docs.docker.com/engine/reference/commandline/cli/).\n1. Create an account on [Docker Hub](https://hub.docker.com), a registry for\n   Docker container images.\n\n\u003e Note: the [`Dockerfile`](./Dockerfile), [`.dockerignore`](./.dockerignore) and\n\u003e [`docker-compose.yml`](./docker-compose.yml) files come included with this\n\u003e repo.\n\n1. Grab the SHA1 commit hash by running the following command in the repo's root\n   folder:\n\n```sh\n# get the SHA1 commit hash of the current branch\ngit rev-parse HEAD\n```\n\n1. Copy the output of the above and paste it as `DENO_DEPLOYMENT_ID` in your\n   .env file. This value is needed to enable caching on Fresh in a Docker\n   deployment.\n\n1. Finally, refer to these guides for using Docker to deploy Deno to specific\n   platforms:\n\n- [Amazon Lightsail](https://deno.land/manual/advanced/deploying_deno/aws_lightsail)\n- [Digital Ocean](https://deno.land/manual/advanced/deploying_deno/digital_ocean)\n- [Google Cloud Run](https://deno.land/manual/advanced/deploying_deno/google_cloud_run)\n\n### Set Up Stripe for Production (Optional)\n\n1. [Activate your Stripe account](https://stripe.com/docs/account/activate).\n1. Navigate to the\n   [**API keys** page](https://dashboard.stripe.com/test/apikeys) on the\n   **Developers** dashboard.\n1. In the **Standard keys** section, click **Reveal test key** on the **Secret\n   key** table row.\n1. Click to copy the value and paste to your `STRIPE_SECRET_KEY` environment\n   variable in your production environment.\n   ```bash\n   STRIPE_SECRET_KEY=\u003cStripe secret key\u003e\n   ```\n1. Navigate to the [**Webhooks** page](https://dashboard.stripe.com/webhooks) to\n   register your webhook endpoint.\n1. Click **Add endpoint**.\n1. Set **Endpoint URL** to your production URL with the `/api/stripe-webhooks`\n   path. E.g. `https://hunt.deno.land/api/stripe-webhooks`.\n1. Set **Listen to** to `Events on your account`.\n1. Set `customer.subscription.created` and `customer.subscription.deleted` as\n   events to listen to.\n1. Click **Add endpoint**.\n1. Optionally,\n   [set up your Stripe branding](https://dashboard.stripe.com/settings/branding)\n   to customize the look and feel of your Stripe checkout page.\n\n### Google Analytics (Optional)\n\nSet `GA4_MEASUREMENT_ID` in your production environment to enable Google\nAnalytics.\n\n\u003e Note: it is not recommended to set this locally, otherwise your tests and\n\u003e debugging requests will be logged.\n\n## REST API Reference\n\n### `GET /api/items`\n\nGet all items in chronological order. Add `?cursor=\u003ccursor\u003e` URL parameter for\npagination. Limited to 10 items per page.\n\nExample 1:\n\n```jsonc\n// https://hunt.deno.land/api/items\n{\n  \"values\": [\n    {\n      \"id\": \"01HAY7A4ZD737BHJKXW20H59NH\",\n      \"userLogin\": \"Deniswarui4\",\n      \"title\": \"czxdczs\",\n      \"url\": \"https://wamufx.co.ke/\",\n      \"score\": 0\n    },\n    {\n      \"id\": \"01HAD9KYMCC5RS2FNPQBMYFRSK\",\n      \"userLogin\": \"jlucaso1\",\n      \"title\": \"Ok\",\n      \"url\": \"https://github.com/jlucaso1/crunchyroll-quasar\",\n      \"score\": 0\n    },\n    {\n      \"id\": \"01HA7YJJ2T66MSEP78NAG8910A\",\n      \"userLogin\": \"BrunoBernardino\",\n      \"title\": \"LockDB: Handle process/event locking\",\n      \"url\": \"https://lockdb.com\",\n      \"score\": 2\n    }\n    // 7 more items...\n  ],\n  \"cursor\": \"AjAxSDdUNTBBUkY0QzhEUjRXWjkyVDJZSFhZAA==\"\n}\n```\n\nExample 2 (using `cursor` field from page 1):\n\n```jsonc\n// https://hunt.deno.land/api/items?cursor=AjAxSDdUNTBBUkY0QzhEUjRXWjkyVDJZSFhZAA==\n{\n  \"values\": [\n    {\n      \"id\": \"01H777YG17VY8HANDHE84ZXKGW\",\n      \"userLogin\": \"BrunoBernardino\",\n      \"url\": \"https://asksoph.com\",\n      \"title\": \"Ask Soph about a dead philosopher\",\n      \"score\": 2\n    },\n    {\n      \"id\": \"01H6RG2V3AV82FJA2VY6NJD9EP\",\n      \"userLogin\": \"retraigo\",\n      \"url\": \"https://github.com/retraigo/appraisal\",\n      \"title\": \"Appraisal: Feature Extraction, Feature Conversion in TypeScript\",\n      \"score\": 0\n    },\n    {\n      \"id\": \"01H64TZ3TNKFWS35MJ9PSGNWE1\",\n      \"userLogin\": \"lambtron\",\n      \"url\": \"https://www.zaynetro.com/post/2023-how-deno-works\",\n      \"title\": \"How Deno works (blog post)\",\n      \"score\": 2\n    }\n    // 7 more items...\n  ],\n  \"cursor\": \"AjAxSDJUSlBYWUJRM1g0OEo2UlIzSFgyQUQ0AA==\"\n}\n```\n\n### `GET /api/items/:id`\n\nGet the item with the given ID.\n\nExample:\n\n```jsonc\n// https://hunt.deno.land/api/items/01H5379J1VZ7EB54KSCSQSCRJC\n{\n  \"id\": \"01H5379J1VZ7EB54KSCSQSCRJC\",\n  \"userLogin\": \"lambtron\",\n  \"url\": \"https://github.com/Savory/saaskit-danet\",\n  \"title\": \"saaskit-danet: a modern SaaS template built for Fresh for SSR and Danet for the API\",\n  \"score\": 10\n}\n```\n\n### `GET /api/users`\n\nGet all users in alphabetical order by GitHub login. Add `?cursor=\u003ccursor\u003e` URL\nparameter for pagination. Limited to 10 users per page.\n\nExample 1:\n\n```jsonc\n// https://hunt.deno.land/api/users\n{\n  \"values\": [\n    {\n      \"login\": \"51chengxu\",\n      \"sessionId\": \"9a6745a1-3a46-45c8-a265-c7469ff73678\",\n      \"isSubscribed\": false,\n      \"stripeCustomerId\": \"cus_OgWU0R42bolJtm\"\n    },\n    {\n      \"login\": \"AiridasSal\",\n      \"sessionId\": \"adb25cac-9be7-494f-864b-8f05b80f7168\",\n      \"isSubscribed\": false,\n      \"stripeCustomerId\": \"cus_OcJW6TadIjjjT5\"\n    },\n    {\n      \"login\": \"ArkhamCookie\",\n      \"stripeCustomerId\": \"cus_ObVcWCSYwYOnWS\",\n      \"sessionId\": \"fd8e7aec-2701-44ae-925b-25e17ff288c4\",\n      \"isSubscribed\": false\n    }\n    // 7 more users...\n  ],\n  \"cursor\": \"AkVob3ItZGV2ZWxvcGVyAA==\"\n}\n```\n\nExample 2 (using `cursor` field from page 1):\n\n```jsonc\n// https://hunt.deno.land/api/users?cursor=AkVob3ItZGV2ZWxvcGVyAA==\n{\n  \"values\": [\n    {\n      \"login\": \"EthanThatOneKid\",\n      \"sessionId\": \"ae7425c1-7932-412a-9956-e456787d557f\",\n      \"isSubscribed\": false,\n      \"stripeCustomerId\": \"cus_OeYA2oTJRlZBIA\"\n    },\n    {\n      \"login\": \"Fleury99\",\n      \"sessionId\": \"2e4920a3-f386-43e1-8c0d-61b5e0edfc0d\",\n      \"isSubscribed\": false,\n      \"stripeCustomerId\": \"cus_OcOUJAYmyxZlDR\"\n    },\n    {\n      \"login\": \"FriendlyUser\",\n      \"stripeCustomerId\": \"cus_ObLbqu5gxp0qnl\",\n      \"sessionId\": \"508ff291-7d1c-4a67-b19f-447ad73b5914\",\n      \"isSubscribed\": false\n    }\n    // 7 more users...\n  ],\n  \"cursor\": \"Ak5ld1lhbmtvAA==\"\n}\n```\n\n### `GET /api/users/:login`\n\nGet the user with the given GitHub login.\n\nExample:\n\n```jsonc\n// https://hunt.deno.land/api/users/hashrock\n{\n  \"login\": \"hashrock\",\n  \"stripeCustomerId\": \"cus_ObqbLXkRtsKy70\",\n  \"sessionId\": \"97eec97a-6636-485e-9b14-253bfa3ce1de\",\n  \"isSubscribed\": true\n}\n```\n\n## Goals and Philosophy\n\nFor the user, the website should be fast, secure and have a design with clear\nintent. Additionally, the HTML should be well-structured and indexable by search\nengines. The defining metrics for these goals are:\n\n- A perfect [PageSpeed Insights](https://pagespeed.web.dev/) score.\n- Fully valid HTML, as measured by\n  [W3C's Markup Validation Service](https://validator.w3.org/).\n\nFor the developer, the codebase should minimize the steps and amount of time\nrequired to get up and running. From there, customization and extension of the\nweb app should be simple. The characteristics of a well-written codebase also\napply, such as:\n\n- Easy to understand\n- Modular functionality\n- Clearly defined behavior with validation through tests\n\n## Community and Resources\n\nJoin [the `#saaskit` channel in Deno's Discord](https://discord.gg/deno) to meet\nother SaaSKit developers, ask questions, and get unblocked.\n\nHere's a list of articles, how to guides, and videos about SaaSKit:\n\n- [Announcing Deno SaaSKit](https://deno.com/blog/announcing-deno-saaskit)\n- [Getting Started with SaaSKit (video walkthrough)](https://www.youtube.com/watch?v=1GYs3NbVCfE)\n","funding_links":[],"categories":["TypeScript","Templates","typescript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenoland%2Fsaaskit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdenoland%2Fsaaskit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenoland%2Fsaaskit/lists"}