Skip to content

Drizzle Next 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.

The scaffolded code will be placed into the (development) route group. By default, the pages and actions in this group are only accessible in the development environment. This behavior can be changed in app/(development)/layout.tsx. The recommended workflow is to change the authentication and authorization behavior of these pages and actions before moving the code into a different route group, such as (public) or (private).

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.

Scaffolding Modes

During the init command, you have the option to choose the --framework. This is used to control what type of scaffolding Drizzle Next will generate.

  • next - This is the default option. It will generate everything you need to start building full stack Next.js apps. Use case: If you plan to build full stack Next.js apps using the server components and server actions pattern with Drizzle ORM.
  • express - This mode will generate only Express.js API routes along with the Drizzle ORM schemas. Use case: If you need to quickly generate CRUD APIs that integrates both Express.js and Drizzle ORM.
  • drizzle - This mode will only setup Drizzle ORM. When you run scaffolds, it will only generate the Drizzle tables. Use case: If you want to use Drizzle Next's drizzle automation with other frameworks.
  • all - This option includes all of the above. This mode will generate full stack Next.js code, Drizzle tables, and Express.js API routes. Use case: If you want to build a full stack Next.js app, along with a separate Express API that leverages the same Drizzle tables.

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 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}.

Auth

During initialization of Drizzle Next, you can optionally choose to install Auth.js.

Drizzle Next provides a create-user.ts script to create test users.

The Auth.js configuration is located in lib/auth.ts. By default, Drizzle Next includes the following providers: Credential, GitHub, Google, Postmark, and Nodemailer. You can add or remove providers as needed.

After initialization, Drizzle Next will display a checklist of things to complete setup, such as adding client ids and secrets.

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 at /signin and access a user dashboard at /dashboard.

Any pages in the (private) route group will require the user to have a user role. This behavior can be changed in app/(private)/layout.tsx.

Authorization

If admin was enabled, Drizzle Next will install Drizzle Admin and provide the all of the initial setup required to start using the Drizzle Admin Dashboard.

Users with the admin role will be able to access the admin dashboard at /admin. The admin signin is at /admin-signin.

An authorization check happens at the admin layout.tsx. The lib/authorize.ts module contains an authorize utility function. The function can be customized and used in pages, layouts, and server actions.

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

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

By default, file uploads are placed in a git ignored uploads directory at the root of the project.

An uploads route handler is used for serving the static files.

TIP

Do not use the public directory for uploads because Next.js only generates routes for public files at compile time.

For better performance, consider using a web server like nginx to serve the uploaded files or an s3 compatible bucket.

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 upload.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

experimental

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.

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

Drizzle Next project structure.

text
- app
  - (admin) - route group for the drizzle admin engine
  - (auth) - route group for signin and signout feature
  - (development) - route group where scaffolded code is placed
  - (private) - route group requiring logged in user
  - (public) - route group that is publicly accessible
  - api - api routes
  - uploads - upload route handler for serving uploaded files
- components
  - ui - customizable ui components
- drizzle - sql migrations
- lib - configuration and utilities
- public - static assets
- schema - drizzle schemas
- scripts - executable scripts
- styles - css
- types - module augmentation and types

Scaffold structure

Drizzle Next uses a feature based, instead of type based, structure for scaffolding. The actions, components, and utilities are colocated with the routes. This convention reduces the number of directories that need to be generated. The scaffolded code is meant to be changed and moved around by the developer.

text
- app
  - (development)
    - posts
      - _components
      - _lib
      - [id]
      - new

TIP

All routes in the (development) route group are by default only accessible when the NODE_ENV environment variable is set to development. This prevents accidental exposure of scaffolded code in production.

The scaffolded code can be changed, moved around, deleted, or just saved as a reference.

Awaited Return Types

The _lib folder for each scaffold contains reusable queries and return types.

There are two main advantages to extracting queries into a separate module.

  1. Extracting the query functions makes it reusable in other parts of the code.
  2. It allows us to create a reusable Awaited ReturnType which reduces type boilerplate.

For example, here is a getPostById function:

ts
import { eq } from "drizzle-orm";
import { db } from "@/lib/db";
import { posts } from "@/schema/posts";

export type PostObj = Awaited<ReturnType<typeof getPostById>>;

export async function getPostById(id: string) {
  return await db.query.posts.findFirst({
    where: eq(posts.id, id),
    with: { category: true },
  });
}

The PostObj type is automatically defined by whatever is returned from the getPostById 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 were to write it by hand:

ts
type PostObj = {
  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 { PostObj } from "../_lib/get-post-by-id";

export function PostDetail({ postObj }: { postObj: PostObj }) {
  // ...
}

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.