Go from Zero to Launch in Days: A Quick-Start Boilerplate for Fast Web App Development

image info

Building an app from scratch often involves repetitive tasks like database connections, authentication, payment integration, and styling. By the time you finish setting up these essentials, it's easy to lose sight of your app's original idea!

Mark Lou's ShipFast boilerplate has gained popularity for streamlining this process. It provides all the basic features, allowing developers to focus on building unique app features. If you don't mind adopting Mark's decisions, it might be the quickest way to create an app and I highly recommend it as he has done a lot of work refining this boiler point and making it as smooth of a process as possible to build a web app.

However, I prefer a different tech stack and like to build my own components. This article will explore how to create a custom boilerplate from scratch, perfect for developers building multiple apps. By doing the foundational work once, you’ll have a reusable starting point for every project.

For my tech stack, I will use:

  • Next.js 14 with the App Router
  • CSS Modules
  • Stripe for payments
  • NextAuth for authentication
  • Prisma with CockroachDB for database management

This guide will walk you through creating a boilerplate with these technologies, enabling you to build and ship apps more efficiently. Let's dive into the core components of a great boilerplate and the step-by-step process to create one.


Boilerplate Essentials

When developing a Software as a Service (SaaS) product, there are four essential components you'll need every time:

  1. Authentication: To allow users to sign in and access your app.
  2. Database: To store user information and app data securely.
  3. Email Services: To send payment confirmations or updates.
  4. Payment Provider: To facilitate transactions for your products or services.

These components form the backbone of your app. Depending on your specific needs, you might also want to include additional features like AI models or advanced search capabilities. Understanding these essentials is the first step in building a boilerplate that accelerates your development process.


Choosing Your Tech Stack

The next step is selecting a technology stack that seamlessly integrates these components, providing a robust foundation for future projects. Let's break it down into front-end, back-end, and supporting services:

Front-End

For the front end, JavaScript remains the most popular choice. It provides various frameworks like React, Angular, Svelte, Vue, and SolidJS. Consider choosing a full-stack framework such as Next.js for React or Nuxt for Vue, which simplifies server-side rendering and API management.

Why Next.js?

  • Server-Side Rendering (SSR): Improves performance and SEO.
  • Static Site Generation (SSG): For fast-loading pages.
  • File-Based Routing: With App Router in Next.js 14, managing routes is more intuitive.
  • Built-In API Routes: Simplifies backend integration.
  • Flexible Styling Options: Use CSS Modules, TailwindCSS, or any other styling preference.

CSS Framework

While CSS Modules will be our primary styling tool, consider integrating a UI library like TailwindCSS, DaisyUI, or Shadcn for rapid component development.

Back-End

For the back-end, you have two primary database options:

  • SQL-Based Databases: Use PostgreSQL or MySQL for structured data.
  • Document-Based Databases: Consider MongoDB or Firebase for flexibility.

Why Prisma with CockroachDB?

  • Prisma: An ORM that streamlines database management with type-safe queries and migrations.
  • CockroachDB: A distributed SQL database designed for global scale and resilience, offering robust ACID transactions.

Authentication

For authentication, NextAuth.js is a great choice, offering flexibility and ease of integration with various providers. Alternatively, Firebase provides built-in authentication services if you're leveraging their platform.

Payments

Stripe remains the leading choice for payments, known for its comprehensive API and robust features. However, alternatives like Lemon Squeezy offer competitive options. Explore their documentation to see what suits your needs best.

Email Services

To handle transactional emails, consider providers like Mailgun, SendGrid, or Postmark. Each offers unique features for reliability and scalability.


Preparing for Setup

Before diving into the code, let's establish a structured approach to streamline the setup process. Here are some preparatory steps:

  1. Documentation: Create a Notion document or markdown file to list essential npm commands and setup instructions. This will be a valuable resource for quickly bootstrapping new projects.

  2. Dependencies: Note down npm install commands for key packages:

    npx create-next-app@14 your-app-name
    npm install dotenv next-auth @prisma/client @stripe/stripe-js tailwindcss css-modules
  3. Configuration Files: Prepare configuration templates for various services, such as:

    • Prisma Schema: Define your database schema.
    • Stripe Setup: Configure API keys and webhook handlers.
    • NextAuth.js: Set up providers and session management.
    • Environment Variables: Store sensitive information securely in .env files, ensuring they are excluded from version control using .gitignore.
  4. UI Libraries: Decide on CSS frameworks or UI libraries (like TailwindCSS or DaisyUI) to enhance your styling capabilities.

  5. Git Repository: Initialize a Git repository to version control your code and facilitate collaboration.

These preparations ensure a smooth setup process, allowing you to focus on building features without unnecessary hurdles.


Example Structure

Now, let's delve into a typical project structure that leverages the technologies we've chosen:

your-app-name/
├── prisma/
│   ├── schema.prisma    # Prisma schema for CockroachDB
│   └── migrations/      # Database migration files
├── public/
│   └── assets/          # Static assets like images and fonts
├── src/
│   ├── app/
│   │   ├── api/         # API routes (e.g., Stripe, NextAuth)
│   │   ├── auth/        # Authentication logic
│   │   ├── components/  # Reusable UI components
│   │   ├── styles/      # CSS Modules and global styles
│   │   ├── pages/       # App routes using Next.js App Router
│   │   ├── utils/       # Utility functions
│   │   ├── lib/         # Configuration files (e.g., Stripe, Prisma client)
│   │   └── server/      # Server-side logic
│   │
│   ├── middleware.ts    # Middleware for authentication and other logic
│   ├── env.d.ts         # TypeScript types for environment variables
│   └── ...
├── .env.example         # Template for environment variables
├── next.config.js       # Next.js configuration
├── package.json         # Project dependencies and scripts
└── ...

Focus Areas

  1. Authentication and Database Configuration: Start by configuring authentication with NextAuth and your database using Prisma. This involves setting up providers for NextAuth and defining your database schema in prisma/schema.prisma.

  2. Environment Variables: Ensure API keys and other sensitive information are stored in .env files. Use libraries like dotenv to access these variables in your application.

  3. API Routes: Implement API routes for Stripe payments, such as checkout and webhooks, within the src/app/api/ directory.

  4. Frontend Development: Develop UI components with CSS Modules and integrate them into your app's pages.

  5. Git Setup: Initialize a Git repository to version control your code and make it easily accessible for future projects. Use .gitignore to exclude sensitive files like .env.


Creating the Boilerplate

Let's go through the step-by-step process of creating this boilerplate:

Step 1: Set Up Your Project

Start by creating a new Next.js project and installing necessary dependencies:

npx create-next-app@14 your-app-name
cd your-app-name
npm install dotenv next-auth @prisma/client @stripe/stripe-js tailwindcss postcss autoprefixer

Set up TailwindCSS:

npx tailwindcss init -p

Configure tailwind.config.js:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Create a globals.css file in the src/styles directory and import TailwindCSS:

@tailwind base;
@tailwind components;
@tailwind utilities;

Step 2: Configure Prisma with CockroachDB

Initialize Prisma and configure it with CockroachDB:

npx prisma init

Edit prisma/schema.prisma:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "cockroachdb"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(uuid())
  email     String   @unique
  password  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  // Add more fields as needed
}

model Subscription {
  id           String   @id @default(uuid())
  userId       String
  user         User     @relation(fields: [userId], references: [id])
  stripeId     String   @unique
  status       String
  priceId      String
  quantity     Int
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt
  // Add more fields as needed
}

Generate Prisma Client:

npx prisma generate

Step 3: Set Up NextAuth for Authentication

Create an authentication route in src/app/api/auth/[...nextauth].ts:

import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import prisma from "../../../lib/prisma";

export default NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    Providers.Email({
      server: process.env.EMAIL_SERVER,
      from: process.env.EMAIL_FROM,
    }),
    Providers.Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
    // Add more providers as needed
  ],
  database: process.env.DATABASE_URL,
  session: {
    jwt: true,
  },
  callbacks: {
    async session(session, user) {
      session.userId = user.id;
      return session;
    },
  },
});

Step 4: Configure Stripe for Payments

Create a Stripe configuration file in src/lib/stripe.ts:

import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: "2020-08-27",
});

export default stripe;

Implement API routes for handling payments in src/app/api/stripe/:

// src/app/api/stripe/checkout.ts
import { NextApiRequest, NextApiResponse } from "next";
import stripe from "../../../lib/stripe";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === "POST") {
    try {
      const { items, email } = req.body;
      const session = await stripe.checkout.sessions.create({
        payment_method_types: ["card"],
        line_items: items,
        mode: "payment",
        success_url: `${req.headers.origin}/success`,
        cancel_url: `${req.headers.origin}/cancel`,
        customer_email: email,
      });

      res.status(200).json({ id: session.id });
    } catch (err) {
      res.status(500).json({ error: err.message });
    }
  } else {
    res.setHeader("Allow", "POST");
    res.status(405).end("Method Not Allowed");
  }
}

Step 5: Set Up Environment Variables

Create a .env.local file with your environment variables:

DATABASE_URL="your_cockroachdb_connection_string"
STRIPE_SECRET_KEY="your_stripe_secret_key"
EMAIL_SERVER="your_email_server"
EMAIL_FROM="your_email_from"
GOOGLE_CLIENT_ID="your_google_client_id"
GOOGLE_CLIENT_SECRET="your_google_client_secret"
NEXTAUTH_URL="http://localhost:3000"

Step 6: Build Front-End Components

Create reusable UI components in src/app/components/. For example, a Button component using CSS Modules:

// src/app/components/Button.tsx
import styles from "./Button.module.css";

type ButtonProps = {
  text: string;
  onClick: () => void;
};

export const Button = ({ text, onClick }: ButtonProps) => (
  <button className={styles.button} onClick={onClick}>
    {text}
  </button>
);

Define styles in src/app/components/Button.module.css:

.button {
  background-color: #0070f3;
  color: white;
  padding: 10px 20px;
  border-radius: 5px;
  border: none;
  cursor: pointer;
  transition: background-color 0.3s;
}

.button:hover {
  background-color: #005bb5;
}

Step 7: Implement Authentication Middleware

Create a middleware file in src/middleware.ts to protect routes:

import { withAuth } from "next-auth/middleware";

export default withAuth({
  pages: {
    signIn: "/auth/signin",
  },
});

export const config = { matcher: ["/protected-route"] };

Step 8: Initialize Git and Push to Repository

Finally, initialize Git and push your code to a repository:

git init
git add .
git commit -m "Initial commit"
git remote add origin your-repo-url
git push -u origin master

Conclusion

With your boilerplate ready, you can rapidly start building new applications. This setup not only saves time but ensures a strong foundation with modern tools and practices. You can now focus on creating innovative features, confident that the essential components are already in place.

Happy Coding! 🚀

Next Steps

Consider the following enhancements to your boilerplate:

  • Add Custom Hooks: Create reusable hooks for state management and API calls.
  • Integrate Testing Frameworks: Use Jest or Cypress for unit and integration testing.
  • Optimize Performance: Implement caching strategies and optimize images for faster load times.
  • Enhance Security: Implement advanced authentication and data protection measures.
  • Explore CI/CD: Set up continuous integration and deployment pipelines for automated testing and deployment.

By continuously improving your boilerplate, you'll build a toolkit that evolves with your needs, streamlining future projects and enhancing your productivity.

Picture of Derek Brumby

WRITTEN BY

Derek Brumby

← View all posts