Blog
SaaS17 min czytania

Open SaaS — darmowy starter SaaS ze Stripe, autoryzacją i AI

Szablon SaaS na licencji MIT z uwierzytelnianiem, trzema dostawcami płatności, panelem administracyjnym, S3, mailami, zadaniami w tle i testami Playwright — postawiony jedną komendą. Rozbieramy jednak rzecz, którą łatwo przeoczyć: to nie jest boilerplate, a szablon dla Waspa, frameworka, który kompiluje całą aplikację i jest wciąż w becie. Analizujemy, co z tego wynika, dlaczego skille dla agentów są tu warunkiem użyteczności, i porównujemy uczciwie z tym, co daje Laravel z Cashierem.

Każdy produkt SaaS zaczyna się od tej samej listy. Rejestracja z weryfikacją adresu e-mail. Logowanie przez Google, bo połowa ludzi nie chce zakładać kolejnego hasła. Płatności z subskrypcjami, obsługą webhooków i tym, co się dzieje, gdy karta przestaje działać. Panel administracyjny. Maile transakcyjne. Wgrywanie plików. Zadania w tle. Strona lądowania. Analityka.

To dwa, może trzy tygodnie pracy przed powstaniem czegokolwiek, co jest właściwym produktem — i praca, którą większość zespołów robiła już kilka razy, za każdym razem trochę inaczej. Stąd popularność szablonów SaaS, których w ostatnich latach powstały dziesiątki, w większości płatne.

Open SaaS (github.com/wasp-lang/open-saas) jest darmowy, na licencji MIT, i ma w środku całą tę listę. Ale zawiera też jedną rzecz, którą łatwo przeoczyć, a która przesądza o wartości całej oceny: to nie jest boilerplate w zwykłym sensie. Open SaaS jest szablonem dla Waspa — frameworka, który nie jest biblioteką dodawaną do projektu, a warstwą, która kompiluje aplikację.

Decyzja o Open SaaS jest więc w istocie decyzją o Waspie. Poniżej: co jest w szablonie, czym dokładnie jest Wasp i co z jego natury wynika, dlaczego skille dla agentów kodujących są tu wyjątkowo istotne — i uczciwe porównanie z tym, co w naszym stacku daje Laravel z Cashierem.

Stan obu projektów

Dane z API GitHuba na 24 września 2026. Open SaaS:

  • 15 766 gwiazdek i 1890 forków, licencja MIT,
  • repozytorium założone 1 grudnia 2023, ostatni push 6 sierpnia 2026, 104 otwarte zgłoszenia,
  • cztery commity od 25 lipca — i wszystkie dotyczą wersjonowania dokumentacji pod nową specyfikację Waspa 0.24.

Ta ostatnia liczba wymaga właściwej interpretacji: szablon nie potrzebuje codziennych commitów, bo jego zadaniem jest być punktem startowym, a nie zależnością. Pokazuje jednak, w jakim rytmie żyje — nie własnym, a rytmem frameworka pod spodem.

Wasp:

  • 18 726 gwiazdek, licencja MIT, kod w TypeScripcie,
  • repozytorium założone 30 stycznia 2020 — sześć lat rozwoju, ostatni commit z 9 września 2026,
  • najnowsze wydanie v0.25.0 z 27 lipca 2026, poprzedzone kandydatem; wcześniej v0.24.0 z 11 czerwca i v0.23.0 z 15 kwietnia — czyli wydanie minor raz na sześć–osiem tygodni,
  • 843 otwarte zgłoszenia.

Warto od razu zauważyć rozjazd: przykładowy kod w materiałach Open SaaS wskazuje wasp: { version: "^0.24.0" }, a sam Wasp jest już na 0.25.0. Szablon podąża za frameworkiem z opóźnieniem — co jest normalne, ale oznacza, że wydanie nowego Waspa nie znaczy jeszcze, że szablon jest na nie gotowy.

Czym dokładnie jest Wasp

Nazwa jest skrótem od Web Application Specification, a projekt opisuje się jako framework w stylu Rails dla Reacta, Node.js i Prismy. Sposób pracy jest jednak inny niż we wszystkich trzech tych technologiach osobno.

Aplikację opisuje się w pliku main.wasp.ts, importując definicje z pakietu @wasp.sh/spec:

// main.wasp.ts
import { app, page, query, route } from "@wasp.sh/spec";

import { MainPage } from "./src/MainPage" with { type: "ref" };
import { DashboardPage } from "./src/dashboard/DashboardPage" with { type: "ref" };
import { getTasks } from "./src/queries" with { type: "ref" };

export default app({
  name: "todoApp",
  wasp: { version: "^0.24.0" },
  title: "ToDo App",
  auth: {
    userEntity: "User",
    methods: {
      email: { /* ... */ },
      google: { /* ... */ },
    },
    onAuthFailedRedirectTo: "/login",
  },
  spec: [
    route("RootRoute", "/", page(MainPage)),
    route("DashboardRoute", "/dashboard", page(DashboardPage, {
      authRequired: true,        // dostęp tylko dla zalogowanych
    })),
    query(getTasks, {
      entities: ["Task"],        // automatyczna inwalidacja cache
    }),
  ],
});

Model danych opisuje zwykły schemat Prismy, a cała pozostała logika to normalny React i Node.js, referencjonowany ze specyfikacji.

I tu jest rzecz najważniejsza. Na podstawie tej specyfikacji i naszych plików źródłowych kompilator Waspa generuje cały kod aplikacji w docelowym stacku — frontend, backend i wdrożenie. To nie jest biblioteka, którą się dodaje do projektu. To warstwa, która projekt wytwarza.

Konsekwencja jest podwójna. Z jednej strony dostajemy rzeczy, które w zwykłym projekcie kosztują dni pracy: authRequired: true w jednej linii chroni trasę po obu stronach, entities w zapytaniu daje automatyczną inwalidację cache po zmianie danych, a zadania cykliczne definiuje się funkcją w konfiguracji. Z drugiej — diagnozowanie problemu wymaga rozumienia dwóch warstw: naszego kodu i tego, co Wasp z niego wygenerował.

Do tego dochodzi pełna typizacja end-to-end: funkcje backendu typuje się raz, a typy na frontendzie są wnioskowane automatycznie, bez instalowania ani konfigurowania czegokolwiek. Typowane są też linki, więc zmiana ścieżki trasy wywala błąd kompilacji tam, gdzie ktoś do niej odwołuje się starą nazwą. Przy pracy w monorepo z ręcznie utrzymywanymi typami API to jest różnica, którą czuje się codziennie.

Status projektu: beta. README mówi to wprost i dodaje, że większość funkcji jest w pełni rozwinięta i działa dobrze, ale należy oczekiwać wielu zmian i usprawnień. Docelowy stack jest jeden: React z TanStack Query, Node.js z Expressem i Prisma — wsparcie dla innych jest planem, nie faktem.

Co jest w szablonie

Podział wartości jest tu ciekawy, bo część funkcji przychodzi z frameworka, a część z samego szablonu — i warto wiedzieć, co skąd, bo to decyduje, co zostanie po ewentualnej zmianie decyzji.

Z Waspa:

  • pełne uwierzytelnianie — e-mail z weryfikacją plus logowanie przez Google, GitHuba, Slacka i Microsoft, konfigurowane w kilku linijkach,
  • typizacja end-to-end wraz z typowanymi linkami,
  • zadania w tle i cron — definiowane funkcją w pliku konfiguracyjnym,
  • wdrożenie jedną komendą — baza, serwer i klient na Railway albo Fly.io przez CLI; poza tymi dwoma dostawcami wdraża się ręcznie.

Z szablonu Open SaaS:

  • Płatności u trzech dostawców do wyboru — Stripe, Polar.sh albo Lemon Squeezy. Obecność Polara jest tu godna odnotowania, bo przy sprzedaży cyfrowej z Unii bierze on na siebie rozliczenie podatku VAT jako sprzedawca formalny,
  • ShadCN UI jako warstwa komponentów, wraz z gotowym panelem administracyjnym,
  • strona lądowania oraz dokumentacja i blog na Astro Starlight — czyli marketing i treści nie wymagają osobnego projektu,
  • analityka — Plausible albo Google Analytics,
  • przykład integracji z OpenAI, z wywoływaniem funkcji,
  • wgrywanie plików na S3,
  • wysyłka maili — SendGrid, Mailgun albo zwykły SMTP,
  • testy end-to-end w Playwright — rzecz, której w darmowych szablonach zwykle nie ma.

Uruchomienie to dwie komendy:

npm i -g @wasp.sh/wasp-cli
wasp new -t saas

Druga z nich tworzy czystą kopię szablonu w nowym katalogu. To sformułowanie z dokumentacji warto zapamiętać, bo opisuje model współpracy: szablon jest kopiowany raz i od tego momentu jest nasz. Nie jest zależnością, którą podbija się w pliku pakietów — a to znaczy, że pobieranie późniejszych aktualizacji szablonu jest osobnym zadaniem, na które dokumentacja ma dedykowany rozdział.

Jak szablon jest zorganizowany

Struktura katalogów mówi o jakości szablonu więcej niż lista funkcji, więc warto do niej zajrzeć. Repozytorium dzieli się na trzy części: app (aplikacja), blog (Astro Starlight) i e2e-tests (Playwright). Wewnątrz aplikacji moduły są rozdzielone po domenach:

template/app/src/
├── admin/          # panel administracyjny
├── analytics/      # Plausible albo Google
├── auth/           # logowanie, rejestracja, hooki
├── client/         # warstwa wspólna interfejsu
├── demo-ai-app/    # przykład integracji z OpenAI
├── file-upload/    # S3
├── landing-page/
├── payment/        # trzy bramki płatności
├── server/
├── shared/
├── user/
└── env.ts

Najciekawsze jest jednak coś, czego z tej listy nie widać: specyfikacja Waspa jest rozdzielona na moduły. Katalog płatności zawiera własny payment.wasp.ts, panel administracyjny — admin.wasp.ts, uwierzytelnianie — auth.wasp.ts. Nie ma więc jednego wielkiego pliku konfiguracyjnego, w którym z czasem nikt się nie orientuje; każda funkcja deklaruje swoje trasy, zapytania i akcje obok własnego kodu. To jest decyzja, którą warto skopiować niezależnie od Waspa.

Warstwa płatności: co realnie oznacza „trzy bramki do wyboru"

Wcześniej napisaliśmy, że wybór dostawcy płatności jest praktycznie jednorazowy. Warto to uściślić, bo szablon rozwiązuje ten problem lepiej, niż sugeruje ostrzeżenie — choć nie do końca.

W katalogu płatności znajduje się interfejs abstrahujący dostawcę:

export interface PaymentProcessor {
  id: "stripe" | "lemonsqueezy" | "polar";
  createCheckoutSession: (args: CreateCheckoutSessionArgs) => Promise<{ session: { id: string; url: string } }>;
  fetchCustomerPortalUrl: (args: FetchCustomerPortalUrlArgs) => Promise<string | null>;
  webhook: PaymentsWebhook;
  webhookMiddlewareConfigFn: MiddlewareConfigFn;
  fetchTotalRevenue: () => Promise<number>;
}

export const paymentProcessor: PaymentProcessor = stripePaymentProcessor;
// export const paymentProcessor: PaymentProcessor = lemonSqueezyPaymentProcessor;
// export const paymentProcessor: PaymentProcessor = polarPaymentProcessor;

Sześć metod, trzy implementacje w podkatalogach i jedna linia decydująca, która jest aktywna. To jest porządna abstrakcja — obejmuje nie tylko utworzenie sesji płatności, ale też portal klienta (gdzie użytkownik sam zmienia plan i kartę), webhook razem z konfiguracją jego middleware oraz sumowanie przychodu na potrzeby panelu administracyjnego. Czyli dokładnie te cztery rzeczy, o których przy własnej integracji zapomina się w tej kolejności.

Komentarz w kodzie mówi jednak wprost, jaki jest zamysł: wybierz procesor, którego chcesz użyć, a potem usuń kod pozostałych z katalogu płatności. Szablon nie jest więc przełącznikiem między dostawcami w czasie działania, a trzema gotowymi implementacjami, z których wybieramy jedną i sprzątamy resztę. Zmiana po roku jest wtedy pracą — ale pracą ograniczoną do sześciu metod o znanym kontrakcie, a nie przepisywaniem połowy aplikacji. To istotna różnica i argument na plus dla szablonu.

W katalogu uwierzytelniania jest analogiczny detal wart odnotowania: plik userSignupFields.ts oraz katalog hooks/. Pierwszy definiuje, co zapisujemy o użytkowniku przy rejestracji, drugi pozwala wpiąć własną logikę w cykl życia uwierzytelniania — czyli dwa miejsca, w których typowy szablon każe grzebać w wygenerowanym kodzie, a tutaj są wyprowadzone na wierzch.

Walidacja zmiennych środowiskowych, którą warto skopiować

Element najbardziej wart uwagi w całym szablonie i całkowicie niezależny od Waspa jako pomysł. Każdy moduł ma własny plik env.ts ze schematem Zoda opisującym zmienne, których potrzebuje:

// src/payment/env.ts
export const paymentPlansSchema = z.object({
  PAYMENTS_HOBBY_SUBSCRIPTION_PLAN_ID: z.string({
    error: "PAYMENTS_HOBBY_SUBSCRIPTION_PLAN_ID is required",
  }),
  PAYMENTS_PRO_SUBSCRIPTION_PLAN_ID: z.string({ /* ... */ }),
  PAYMENTS_CREDITS_10_PLAN_ID: z.string({ /* ... */ }),
});

A schemat główny scala je wszystkie w jeden:

// src/env.ts
export const serverEnvValidationSchema = defineEnvValidationSchema(
  z.object({
    ...authEnvSchema.shape,
    ...stripeEnvSchema.shape,
    ...lemonSqueezyEnvSchema.shape,
    ...polarEnvSchema.shape,
    ...demoAiAppEnvSchema.shape,
    ...fileUploadEnvSchema.shape,
    ...plausibleEnvSchema.shape,
    ...googleAnalyticsEnvSchema.shape,
  }),
);

Wasp scala ten schemat z własnymi walidacjami i sprawdza zmienne środowiskowe przy starcie serwera, a nie w momencie, w którym któraś z nich jest pierwszy raz potrzebna. Do wartości sięga się przez zwalidowany obiekt (import { env } from 'wasp/server'), a nie przez process.env — więc literówka w nazwie zmiennej jest błędem typu, nie undefined, które ujawni się przy pierwszej płatności klienta.

Komentarz w tym pliku zawiera przy tym instrukcję, której zwykle brakuje: usuwając funkcję (na przykład jednego dostawcę analityki albo płatności), trzeba usunąć także jej schemat — inaczej aplikacja będzie wymagać zmiennych, których już nikt nie używa, i nie wstanie bez nich.

To jest wzorzec przenośny do dowolnego stacku. W Laravelu odpowiednikiem jest walidacja konfiguracji przy bootowaniu aplikacji zamiast wywołań env() rozsianych po kodzie — z tą samą korzyścią: aplikacja nie startuje z niekompletną konfiguracją, zamiast wywalić się w środku procesu płatności. Warto to zapamiętać, nawet nie tykając Waspa.

Warstwa dla agentów kodujących

Open SaaS reklamuje się jako gotowy do pracy z asystentami: ma dedykowany AGENTS.md, własne skille i reguły oraz wtyczkę do Claude Code, a sam Wasp dostarcza osobne, oficjalne wtyczki agentowe dla Cursora, Claude Code i innych narzędzi.

To już czwarty projekt w tej serii z tym wzorcem — po Prowlerze, szablonie storefrontu Saleora i ToolJecie. Ale w tym przypadku uzasadnienie jest wyraźnie mocniejsze niż w pozostałych i warto je nazwać wprost.

Wasp ma własny plik specyfikacji, którego model nie zna z treningu w takim stopniu jak Reacta czy Expressa. Bez instrukcji asystent zrobi rzecz, która wygląda na poprawną i jest kompletnie obok: napisze zwykły React z ręcznym routingiem, własną obsługą sesji i osobnym klientem HTTP — czyli zbuduje aplikację obok frameworka, ignorując wszystko, po co ten framework istnieje. Nie będzie to błąd składniowy, tylko cichy powrót do punktu wyjścia.

W tym układzie skille nie są udogodnieniem, a warunkiem, żeby asystent był w ogóle użyteczny. I jest to obserwacja przenośna: im bardziej projekt odchodzi od konwencji, których model nauczył się z publicznego kodu, tym bardziej instrukcje w repozytorium przestają być dodatkiem i stają się częścią interfejsu projektu.

Ocena dla zespołu pracującego w Laravelu

Trzeba to powiedzieć wprost: to nie jest stack, w którym pracujemy. Node.js z Prismą i kompilatorem Waspa to inny świat niż PHP z Eloquentem, a wpis ten nie jest rekomendacją wdrożenia. Jest materiałem do decyzji, gdy pojawi się projekt wymagający backendu w Node albo klient przychodzący z produktem opartym o ten stack.

Kiedy Open SaaS ma sens:

  • własny produkt na wewnętrzny eksperyment, gdzie liczy się czas dojścia do pierwszej działającej płatności, a nie perspektywa pięcioletniego utrzymania,
  • zespół w całości w TypeScripcie — wtedy jeden język od bazy do przeglądarki i typizacja end-to-end są realną przewagą,
  • walidacja pomysłu przed decyzją o technologii docelowej. Szablon MIT, którego nie trzeba nikomu tłumaczyć ani rozliczać, jest do tego dobrym narzędziem.

Kiedy nie: gdy produkt ma żyć lata, a zespół zna PHP. Wasp jest w becie i kompiluje aplikację, więc jego zmiana w wersji minor może wymagać migracji — a dowodem, że to nie jest teoretyczne ryzyko, są właśnie ostatnie commity Open SaaS, w całości poświęcone dostosowaniu dokumentacji do nowej specyfikacji.

Uczciwe porównanie: to samo w naszym stacku

Dla naszego czytelnika właściwym punktem odniesienia nie jest inny szablon w Node, a to, co daje Laravel — i wypada powiedzieć, że daje bardzo podobny zakres:

  • uwierzytelnianie z weryfikacją maila, dwuskładnikowym logowaniem i sesjami — Jetstream albo Fortify, a logowanie społecznościowe przez Socialite,
  • subskrypcje i płatnościCashier, z obsługą webhooków Stripe'a, okresów próbnych, zmian planu i nieudanych obciążeń jako rzeczami wbudowanymi, nie do napisania,
  • panel administracyjny — Filament, o którym pisaliśmy przy ToolJecie, z pełnym dostępem do modeli i polityk,
  • kolejki i harmonogram — w rdzeniu frameworka, z Horizonem do podglądu,
  • pliki, maile, powiadomienia — abstrakcje dysków i sterowników pocztowych w standardzie.

Kluczowa różnica nie leży w liście funkcji, a w dojrzałości fundamentu. Laravel jest po wielu wersjach głównych, z przewidywalnym cyklem wydań i ekosystemem, w którym na każde pytanie istnieje odpowiedź. Wasp jest w wersji 0.25 i sam opisuje się jako beta. Dla produktu, który ma być utrzymywany, to jest różnica ważniejsza niż to, czy szablon ma gotową stronę lądowania.

Uczciwie jednak trzeba dodać, czego w naszym stacku nie ma: typizacji end-to-end między backendem i frontendem bez utrzymywania warstwy pośredniej. Przy Inertii propsy z kontrolera trafiają do komponentu bez kontraktu, który sprawdzi kompilator — i to jest realna przewaga podejścia Waspa, którą warto docenić, nawet nie zmieniając stacku.

Pułapki

  • Wasp jest w becie i kompiluje aplikację. To najważniejsza informacja w całym wpisie i podstawa każdej decyzji. Wersja 0.25, wydania minor co sześć–osiem tygodni, projekt sam zapowiada dalsze zmiany,
  • 843 otwarte zgłoszenia w Waspie — przy frameworku, który generuje kod, część problemów jest z definicji trudna do obejścia własnymi siłami,
  • Szablon podąża za frameworkiem z opóźnieniem. Nowe wydanie Waspa nie oznacza, że Open SaaS jest na nie gotowy,
  • Szablon kopiuje się raz i staje się nasz. Późniejsze pobieranie zmian z góry to osobna praca, nie aktualizacja zależności,
  • Wdrożenie jedną komendą działa na Railway i Fly.io. Na własnym VPS-ie albo w Kubernetesie trzeba wdrażać ręcznie,
  • Wybór dostawcy płatności jest praktycznie jednorazowy. Trzy opcje w szablonie to wygoda na starcie, nie przełącznik na później — model danych, webhooki i logika subskrypcji różnią się między nimi,
  • Bez skilli asystent będzie pisał obok frameworka — i będzie to kod, który się kompiluje, więc problem wyjdzie dopiero przy review,
  • Diagnostyka obejmuje dwie warstwy — nasz kod i to, co wygenerował kompilator. Przy nietypowym błędzie trzeba zajrzeć do wygenerowanego kodu, a to wymaga zrozumienia, jak Wasp go składa,
  • Docelowy stack jest jeden — React z TanStack Query, Node z Expressem, Prisma. Zapowiadane wsparcie innych stacków jest planem.

Podsumowanie

Open SaaS jest jednym z najlepiej wyposażonych darmowych szablonów SaaS, jakie istnieją, i za nim stoi framework z ciekawym, przemyślanym pomysłem. Ale ocena nie może zatrzymać się na liście funkcji, bo prawdziwa decyzja dotyczy fundamentu. Co z tego wynika:

  • Szablon jest kompletny — uwierzytelnianie z czterema dostawcami społecznościowymi, trzy bramki płatności, panel administracyjny, S3, maile, zadania w tle, strona lądowania, blog na Astro i testy Playwright. Wszystko na MIT, za darmo,
  • Ale wybierając Open SaaS, wybierasz Waspa — a Wasp nie jest biblioteką, tylko kompilatorem generującym całą aplikację,
  • Wasp jest w becie (0.25.0 z lipca 2026), po sześciu latach rozwoju i z 843 otwartymi zgłoszeniami. To fundament obiecujący, ale nie ustabilizowany,
  • Typizacja end-to-end i deklaratywne authRequired czy entities to realne oszczędności, nie marketing — warto je zobaczyć nawet bez planów wdrożenia,
  • Zainstaluj skille i wtyczki agentowe od razu, jeśli pracujesz z asystentem. Przy własnej specyfikacji frameworka to warunek użyteczności, nie ozdoba,
  • Wdrożenie jedną komendą tylko na Railway i Fly.io; wszędzie indziej ręcznie,
  • Dostawcę płatności wybierz świadomie na starcie — Polar.sh warto rozważyć przy sprzedaży z Unii, bo bierze na siebie rozliczenie VAT,
  • W naszym stacku odpowiednikiem jest Laravel z Cashierem, Fortify i Filamentem — ten sam zakres na fundamencie po wielu wersjach głównych, za cenę braku typizacji end-to-end,
  • Skopiuj wzorzec walidacji zmiennych środowiskowych — schemat per moduł, scalony w jeden i sprawdzany przy starcie serwera. To działa w każdym stacku i chroni przed brakiem konfiguracji odkrytym w środku płatności,
  • Specyfikację rozdziel na moduły — szablon trzyma payment.wasp.ts, auth.wasp.ts i admin.wasp.ts obok kodu funkcji, zamiast jednego pliku konfiguracyjnego,
  • Do eksperymentu i walidacji pomysłu: tak. Do produktu, który ma być utrzymywany latami przez zespół znający PHP: raczej nie.

Licencja: zarówno szablon Open SaaS, jak i sam framework Wasp są rozpowszechniane na licencji MIT — permisywnej, bez copyleftu, bez warunków przy komercyjnym użyciu poza zachowaniem noty o prawach autorskich. W praktyce oznacza to pełną swobodę: wolno zbudować na tym zamknięty produkt komercyjny, sprzedawać go, modyfikować szablon bez publikowania zmian, a nawet — jak podkreśla samo README — redystrybuować go dalej. To rzadka i wartościowa własność w kategorii, w której konkurencja jest w większości płatna i licencjonowana per miejsce pracy albo per projekt, z zakazem odsprzedaży. Warto przy tym pamiętać o rozróżnieniu, które przy szablonach bywa mylące: licencja MIT dotyczy kodu szablonu i frameworka, a nie usług, do których szablon się podłącza. Stripe, Polar, SendGrid, OpenAI i S3 mają własne cenniki i regulaminy, a to one, nie licencja, wyznaczają realny koszt uruchomienia produktu na tym fundamencie.