Skip to content
This repository has been archived by the owner on May 14, 2023. It is now read-only.

kuroski/next-project-template

Repository files navigation

Project template

This is a Next.js project bootstrapped with create-next-app with some additions of common dependencies and configuration to start a new project.

I was a bit tired on having to install the same dependencies or comming up with some "base" for authentication/routing/etc, so this template is an all-in-one repo to quickly bootstrap projects.

Within this template you have:

  • chakra as component UI framework
  • trpc e2e solution to handle API requests safely + it integrates with react-query to handle requests
  • zod schema validation with static type inference [1]
  • fp-ts our "utility library" to help us handle some things in a more "functional way"
  • tanstack table library to help us handle tables
  • react-hook-form to help us handle forms + validation [1]
  • prisma our ORM + seeders + authentication + protected routes are already there + zod integration + docker-compose in place for dev database
  • envsafe to make sure you don't accidentally deploy apps with missing or invalid environment variables
  • next-translate i18n solution
  • eslint is already there
  • some examples of common things you usually have to do
    • form + validation + mutations
    • protected routes + routes with simple role validation
    • dark/light mode
  • TODO: testing environmnet set up + mock server for integration tests

Getting Started

First, run the development server:

pnpm i

cp .env.sample .env # and add the missing variables
docker-compose up -d

pnpm dev

Open http://localhost:3000 with your browser to see the result.

Notes

Auth config

Open up lib/auth.ts and go nuts with next-auth configuration

Customise themes

Open up lib/theme.ts and go nuts with chakra configuration

i18n

I'm following the defaults of next-translate, so just follow they're guide

Layouts

While we wayt for Next Layouts rfc...

If you have a common layout, just add to the /layouts folder and wrap your with your pages components

ORM

I'm using prisma here, no customisation, just change the schema, follow they're documentation and you are good to go.

tRPC

I like strong typing, and usually I would use a combination with next-connect + fp-ts + io-ts + react-query, but tRPC is a cool solution that despite not being so "FP-hardcore", it is very cool on how much you can achieve with that.

I actually don't customize anything from they're guide, so if you read them you should be at home

Everything is within /server folder and /lib/trpc.ts file.

If you need new routes, just create a new file within /server/routers/product.ts

import { Prisma } from "@prisma/client";
import { createRouter } from "@/server/create-router";
import { createProtectedRouter } from "@/server/create-protected-router";
import { z } from "zod";

const productRoute = createRouter()
  .query("list", {
    input: z.object({
      name: z.string().nullish(),
    }),
    resolve: async ({ input, ctx }) => {
      const where: Prisma.ProductWhereInput = {
        name: {
          contains: input?.name ?? undefined,
        },
      };

      const [products, productCount] = await Promise.all([
        ctx.prisma.product.findMany({
          where,
        }),
        ctx.prisma.product.count({ where }),
      ]);

      return {
        products,
        productCount,
      };
    },
  })
  // if you want protected routes
  .merge(
    "admin.",
    createProtectedRouter().mutation("create", {
      input: z.object({
        name: z.string().min(1),
        description: z.string().min(1),
      }),
      resolve: async ({ ctx, input }) => {
        const product = await ctx.prisma.product.create({
          data: {
            name: input.name,
            description: input.description,
          },
        });

        return product;
      },
    })
  );

export default productRoute;

Connect it to the main router

// server/routers/_app.ts
import superjson from "superjson";

import { createRouter } from "@/server/create-router";
import healthRoute from "@/server/routers/health";
import productRoute from "@/server/routers/product";

export const appRouter = createRouter()
  .transformer(superjson)
  .merge("health.", healthRoute)
  .merge("product.", productRoute);

export type AppRouter = typeof appRouter;

And then use it:

const Home: NextPageWithAuth = () => {
  const [query, setQuery] = useQueryParams({
    name: StringParam,
  });

  const productQuery = trpc.useQuery(["product.list", { ...query }]);

  // ... from now on just follow the flow https://trpc.io/docs/nextjs#6-make-api-requests
};

Custom features

Paths

Since I'm using TS, you can directly import files through an alias instead of relative path

@/ maps to <project_root>/

Logger

I installed pino as a logger tool which is what is recommended within next.js documentation

Just import /lib/pino.ts and use it

Protected routes

By opening /lib/types.ts you'll see NextPageWithAuth, this is a special type you can give to your pages components to include automatic authentication validation.

I could rely on next-auth middlewares, but I don't want to, I would have to enable JWT option and it seemed straightforward to just solve this in the component layer... that would work for simple projects.

You can check the examples of /pages/only-admin.tsx or /pages/private.tsx files, the system is raw and simple:

import NextLink from "next/link";
import { NextPageWithAuth } from "@/lib/types";

const Private: NextPageWithAuth = () => {
  return <div>This is a private route</div>;
};

Private.auth = true; // just add this property and πŸͺ„

export default Private;

If you need to validate roles, I have added a very naive and simple role system since most times I just needed regular/admin users, then:

import NextLink from "next/link";
import { NextPageWithAuth } from "@/lib/types";

const Private: NextPageWithAuth = () => {
  return <div>This is a private route</div>;
};

Private.auth = {
  can: ["ADMIN"],
}; // just add this property and πŸͺ„

export default Private;

TS will help you out there =D

To understand better how that works, check /pages/_app.tsx, this is just a HoC that checks for that auth property and render a component that checks for authenticated users πŸ˜‰.

Utils

Well... /lib/utils.ts is slim, I just have a function to show a "placeholder image" and to slugify some text, no real reason on why only those two, usually I end up needing that every time πŸ˜…

Components

  • /components/FieldErrors.tsx is a component that... display field errors, just send an react-hook-form error variable and it will handle it (please add the remaining validations/translations if you need)
  • /components/FormSection.tsx I like Laravel Jetstream way on handling forms, so this component just represents a section of a form where you can provide a title and description on what that section is
  • /components/Header.tsx ... is the header of the app, light/dark mode toggle button + user auth login/logout link are there

Why I'm not using all dependencies?

I'm not providing an example with everything, I just need/want eventually handle some operations and I will use fp-ts for example for some things... I just want to already have some dependencies I use by default.

PS

I know, its a lot of dependencies and config, JS/TS world is insane, but yeah, I could build a project with just Next.js or plain JS, go on with Solid/Svelte/etc or go to Laravel not worry with anything πŸ˜‚ ... I could build something strict [1] [2] but until now, this template contains everything you'll probably need (and more) for most projects and it's free, spin up Vercel or Railway, provision a database (maybe with Planetscale) and pay nothing, which is amazing.

Still... It wouldn't be as good as Elm [1] πŸ₯²

"sdds Elm", right Sunsi?

Image: It ain't much, but it's honest work

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published