Skip to content

Guide

This guide covers the usage details and features of Drizzle Next.

Scaffold

A scaffold is all of the starter code, including the UI and data layer, that is required to have a fully functional CRUD application. With scaffolding, you spend less time writing boilerplate code and looking things up in documentation because there is a point of reference to build upon. This frees up time and energy to focus on building the interesting parts of the app.

After the initial configuration is completed from running the init command, you can create full stack scaffolding with the scaffold command.

Given the table and column parameters, this command will generate the Next.js user interface, server actions, server components and client components. And it will also generate the Drizzle database table definition.

The -c option takes a space-separated string of column configurations in the following format: column_name:dataType. The data types map directly to most of the available data types in Drizzle ORM. See Data Types for a list of supported data types.

The id, created_at, and updated_at fields are automatically generated by drizzle-next, and should be omitted from the command.

After scaffolding, you can review the schema and make any necessary changes before running the database migrations.

Example:

bash
npx drizzle-next@latest scaffold products -c title:text price:integer description:text

INFO

The table names will be transformed according to the Naming Conventions in this document.

Skip UI generation

The --no-ui flag allows you to skip the generation of the Next.js routes, pages, components, and actions. This is useful if you want to only scaffold the Drizzle database table without the UI.

Skip DB generation

The --no-db flag allows you to skip the generation of the Drizzle database table. This is useful if you have an existing table and want to only scaffold the routes, pages, components, and actions.

Data types

The following are data types that can be used with the scaffold command. Most of the data types in Drizzle ORM are also supported in drizzle-next.

postgresql data types

integer, smallint, bigint, serial, smallserial, bigserial, boolean, text, varchar, char, numeric, decimal, real, doublePrecision, json, jsonb, time, timestamp, date, uuid

mysql data types

int, tinyint, smallint, mediumint, bigint, real, decimal, double, float, serial, binary, varbinary, char, varchar, text, boolean, date, datetime, time, year, timestamp, json

sqlite data types

integer, real, text, boolean, bigint, timestamp

Primary key strategy

drizzle-next supports the following primary key generation strategies:

  • uuidv4 - Uses the crypto package
  • cuid2 - Uses the @paralleldrive/cuid2 package
  • uuidv7 - Uses the uuidv7 package
  • nanoid - Uses the nanoid package
  • auto_increment - Auto increment (This strategy is not compatible with the Drizzle Adapter for Auth.js)

The strategy that you choose during the init process will be saved in drizzle-next.config.ts. This will be used for the authentication, stripe, and scaffold schemas.

Foreign key constraints

drizzle-next supports adding foreign key constraints using the following special data types:

  • references
  • references_select

This will set up the Drizzle relations and the UI form controls for managing the relations.

For example, a one to many relationship where a post belongs to a category can be set up using the following scaffolds.

First, scaffold the one side of the relationship.

bash
npx drizzle-next@latest scaffold category -c title:text

Second, scaffold the many side of the relationship using one of the references data types below:

References Input

The standard references data type will use an Input component that accepts a foreign key string.

bash
npx drizzle-next@latest scaffold post -c category_id:references title:text

References Select

The references_select data type will use a Select component where you can select from a dropdown list of items.

bash
npx drizzle-next@latest scaffold post -c category_id:references_select title:text

The component will initially show a list of ids, however it is easy to customize by changing the code in the form. For example, changing the react code from {category.id} to {category.title}.

CSS Strategy

TailwindCSS is a popular CSS framework. It ships with Next.js by default. It is also the default and recommended CSS framework of Drizzle Next.

However, there are many other options, such as css, scss, css modules, and other css-in-js solutions.

If you prefer a different option, you can opt out of TailwindCSS by choosing the none css strategy during the init command. This will remove all TailwindCSS styling from the generated code.

This is configurable in the generated drizzle-next.config.ts.

Auth

If auth was enabled during initialization, you will be able to scaffold using a private authorization level. These pages along with the server actions will require a user to be authenticated to access.

drizzle-next provides a create-user.ts script to create test users.

drizzle-next uses the jwt strategy of Auth.js. If you need database sessions, you will have to provide the implementation. Note: the credentials provider only supports the jwt strategy.

Private Dashboard

If auth was enabled, users will be able to sign in and access a user dashboard at /dashboard.

Any pages scaffolded with a private authorization level will be placed into the the (private) route group.

After running a private scaffold, a new link to the resource list page will be added to private-sidebar.tsx.

Users can sign in at /signin.

Admin Dashboard

If admin was enabled, users with the admin role will be able to access the admin dashboard at /admin. The admin signin is at /admin-signin.

You will be able to scaffold using an admin authorization level. The pages will be put into the (admin) route group. These pages along with the server actions will require a user with the admin role to access.

An authorization check happens at the admin layout.tsx. The authorization.ts contains an isAdmin utility function that checks the current session for the admin role.

After running an admin scaffold, a new link to the resource list page will be added to admin-sidebar.tsx.

A grant-admin.ts script is provided to grant users the admin role.

The application uses a role text field to determine the user's role. Any user with an admin role will have admin access.

Users will be assigned a user role when created. This behavior can be changed in auth.ts.

File uploads

drizzle-next supports a file data type. This creates a text db column to store the file path along with a basic ui for uploads to the file system

Example:

bash
npx drizzle-next@latest scaffold media -c title:text image:file video:file

TIP

Next.js only generates routes for public files at compile time. If you need to serve the uploaded files, putting them into the public directory will not work in production without a new build every time.

If the uploaded files need to be served immediately after uploading, consider using a web server like nginx to serve the static files or an s3 compatible bucket instead.

In development, drizzle-next will put the files in public/uploads, so that they can be served during development. This works in development because routes are compiled without running a new build.

In production, drizzle-next will put the files in /var/www/uploads. You'll have to find a way to serve these files. For example, pointing an nginx location /uploads/ to the /var/www/uploads folder. Note: This won't work in serverless environments. If you're using serverless, consider using object storage like s3.

The file URI will be saved to the database. The upload paths can be changed in file-utils.ts.

Example nginx config:

text
server {
      listen 80;
      server_name www.example.com;

       location /uploads/ {
          alias /var/www/uploads/;
          autoindex off;
          try_files $uri $uri/ =404;
       }

       location / {
          proxy_pass http://127.0.0.1:3000/;
       }
}

TIP

The Next.js Image component performs automatic resizing of images. This works well for static images. However, uploaded images will not show up immediately unless you use the unoptimized attribute. Alternatively, you can use a regular img tag.

Add-on Extensions

Add-ons are full stack components that can be added after a project has been initialized.

An add-on extension can be added using the add command.

To see a list of available add-ons, run npx drizzle-next@latest add -h.

The add command will install all necessary UI components, npm dependencies and dev dependencies for the add-on.

Then it will write all of the necessary code for the add-on to your project.

Stripe

bash
npx drizzle-next@latest add stripe

Code will be generated for a one-time purchase, monthly subscription plan, and a dynamic pricing checkout. This includes the webhook, checkout session, and customer portal api endpoints.

A pricing page will be generated. The buttons will initiate a stripe checkout if the user is logged in. If the user is not logged in, they will redirect to the sign in page.

A create-price.ts script is provided to create the initial products on Stripe and on the local database. This is required before using the one-time purchase and subscription plan. You must provide the implementation for fulfulling one-time purchases or provisioning subscriptions. That is marked as TODO in the webhook route.

The dynamic pricing does not require creating and mapping to products on Stripe. Dynamic pricing is useful if you need to generate a custom price, manage products, and manage orders in your own database. You must provide the implementation for creating and processing dynamic orders. That is also marked as TODO in the dynamic checkout session and webhook route.

The checkout sessions will be created with the user's email on first checkout. The user's stripe customer id will be saved in the checkout completed webhook. Subsequent checkouts will use the user's stripe customer id. This ensures that the Stripe invoice records are associated to the same Stripe customer, and allows the user to see the full invoice history in the customer portal.

Any of the code and content can be changed to fit your business model. The goal of this Stripe automation is to provide some minimal integration patterns to use as a starting point.

Tiptap Editor

bash
npx drizzle-next@latest add tiptap

This add-on will install several tiptap dependencies and write the code for a generic tiptap editor component.

After installing the add-on, you'll be able to scaffold with a text_tiptap data type.

For example:

bash
npx drizzle-next@latest scaffold posts -c title:text content:text_tiptap

Project Structure

This is the drizzle-next project structure. The scaffolding automations will write to specific folders and files, so it's recommended to not move things around if you plan to continue using drizzle-next automations.

text
- actions
  - admin - server actions requiring admin authorization
  - auth - server actions for authentication
  - private - server actions requiring logged in user
  - public - server actions that are publicly accessible
- app
  - (admin) - route group requiring admin authorization
  - (auth) - route group for authentication
  - (private) - route group requiring logged in user
  - (public) - route group that is publicly accessible
- components
  - admin - components for admin routes
  - auth - components for authentication
  - layouts - components for dashboard layouts
  - private - components for private routes
  - public - components for public routes
  - ui - ui components
- drizzle - sql migrations
- lib - configuration and utilities
- public - static assets
- queries - reusable queries and return types
- schema - drizzle schemas
- scripts - executable scripts
- styles - css
- types - module augmentation

Queries

The queries folder contains modules with reusable queries and return types from running the scaffold command.

Extracting the query functions makes it reusable by putting it in a shared module. One other advantage is that it allows us to create a reusable Awaited ReturnType. For example:

ts
export type PostsWithRelations = Awaited<
  ReturnType<typeof getPostsWithRelations>
>;

export async function getPostsWithRelations() {
  return await db.query.posts.findMany({
    with: {
      category: true,
    },
  });
}

The PostsWithRelations type is automatically defined by whatever is returned from the getPostsWithRelations function.

This becomes more relevant as your project grows in size and you must deal with more nested relations. Without an automatic return type, you would spend a large amount of time writing types by hand to annotate your React component props.

With the awaited return type, we get a type that might look something like this if we wrote it by hand:

ts
type PostsWithRelations = {
  id: string;
  title: string;
  content: string;
  category: {
    id: string;
    name: string;
  };
}[];

Now we can annotate our components as needed without having to write the type ourself:

tsx
import { PostsWithRelations } from "@/queries/post-queries";

export function PostTable({ postList }: { postList: PostsWithRelations }) {
...
}

This makes it easier to achieve full stack type safety across the front end and back end. Note that Awaited ReturnTypes is a feature of TypeScript and not specific to any library.

Naming conventions

Number and case transformations will be automatically applied to the generated code.

drizzle-next has two options when it comes to naming conventions. Plurize Enabled and Pluralize Disabled.

Case transformations (camel case, snake case, etc) will always be applied, however number transformations (singular/plural/original) will be applied depending on the pluralize mode used.

Original, in this context, means no transformations are applied.

You can change the mode in drizzle-next.config.ts by setting the pluralizeEnabled boolean.

Pluralize Enabled

Pluralized Enabled is the default setting. With pluralize enabled, drizzle-next uses naming conventions as described in the table below.

Regardless of whether you pass in foo_bar or foo_bars as the table name, the number transformations will be applied to each part of the code, along with the case transformation.

Generated CodeNumberCaseExample
Class namessingularpascal caseFooBar
Database table namespluralsnake casefoo_bars
Database column namesoriginalsnake casefoo_bar
Database foreign keyssingularsnake casefoo_bar_id
Drizzle table variable namespluralcamel casefooBars
Drizzle column property namesoriginalcamel casefooBar
Drizzle foreign key property namessingularcamel casefooBarId
Drizzle findMany variable namessingularcamel casefooBarList
Drizzle findFirst variable namessingularcamel casefooBarObj
File namesanykebab casefoo-bar.ts
Form input namesoriginalcamel casefooBar
React array propssingularcamel casefooBarList
React object propssingularcamel casefooBar
URL pathnamesanykebab case/foo-bar
Query string parametersoriginalcamel case?fooBar=baz
UI table and column namesanycapital caseFoo Bar

Pluralize Disabled

With pluralize disabled, drizzle-next will not apply any number transformations to the generated code.

If you pass in foo_bar as the table name, it will always use the singular form.

If you pass in foo_bars as the table name, it will always use the plural form.

Generated CodeNumberCaseSingularPlural
Class namesoriginalpascal caseFooBarFooBars
Database table namesoriginalsnake casefoo_barfoo_bars
Database column namesoriginalsnake casefoo_barfoo_bars
Database foreign keysoriginalsnake casefoo_bar_idfoo_bars_id
Drizzle table variable namesoriginalcamel casefooBarfooBars
Drizzle column property namesoriginalcamel casefooBarfooBars
Drizzle foreign key property namesoriginalcamel casefooBarIdfooBarsId
Drizzle findMany variable namesoriginalcamel casefooBarListfooBarsList
Drizzle findFirst variable namesoriginalcamel casefooBarObjfooBarsObj
File namesanykebab casefoo-bar.tsfoo-bars.ts
Form input namesoriginalcamel casefooBarfooBars
React array propsoriginalcamel casefooBarListfooBarsList
React object propsoriginalcamel casefooBarfooBars
URL pathnamesanykebab case/foo-bar/foo-bars
Query string parametersoriginalcamel case?fooBar=baz?fooBars=baz
UI table and column namesanycapital caseFoo BarFoo Bars

This mode was added to support non-English projects where pluralization may not apply.

Dependency Strategy

Drizzle Next provides a --latest option to install latest dependencies during the init command. This means you'll get the latest cutting edge versions of Drizzle ORM, Auth.js, TailwindCSS, Zod, and other packages. However, you may need to resolve any unexpected issues.

By default, you'll get the pinned versions of each top-level dependency during the init command. The pinned versions can be found in package-pinned.json in the drizzle-next GitHub repo. Each build is tested before latest dependencies are merged as the pinned dependencies.

Released under the MIT License.