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:
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 thecrypto
packagecuid2
- Uses the@paralleldrive/cuid2
packageuuidv7
- Uses theuuidv7
packagenanoid
- Uses thenanoid
packageauto_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.
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.
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.
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:
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:
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
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
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:
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.
- 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:
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:
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:
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 Code | Number | Case | Example |
---|---|---|---|
Class names | singular | pascal case | FooBar |
Database table names | plural | snake case | foo_bars |
Database column names | original | snake case | foo_bar |
Database foreign keys | singular | snake case | foo_bar_id |
Drizzle table variable names | plural | camel case | fooBars |
Drizzle column property names | original | camel case | fooBar |
Drizzle foreign key property names | singular | camel case | fooBarId |
Drizzle findMany variable names | singular | camel case | fooBarList |
Drizzle findFirst variable names | singular | camel case | fooBarObj |
File names | any | kebab case | foo-bar.ts |
Form input names | original | camel case | fooBar |
React array props | singular | camel case | fooBarList |
React object props | singular | camel case | fooBar |
URL pathnames | any | kebab case | /foo-bar |
Query string parameters | original | camel case | ?fooBar=baz |
UI table and column names | any | capital case | Foo 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 Code | Number | Case | Singular | Plural |
---|---|---|---|---|
Class names | original | pascal case | FooBar | FooBars |
Database table names | original | snake case | foo_bar | foo_bars |
Database column names | original | snake case | foo_bar | foo_bars |
Database foreign keys | original | snake case | foo_bar_id | foo_bars_id |
Drizzle table variable names | original | camel case | fooBar | fooBars |
Drizzle column property names | original | camel case | fooBar | fooBars |
Drizzle foreign key property names | original | camel case | fooBarId | fooBarsId |
Drizzle findMany variable names | original | camel case | fooBarList | fooBarsList |
Drizzle findFirst variable names | original | camel case | fooBarObj | fooBarsObj |
File names | any | kebab case | foo-bar.ts | foo-bars.ts |
Form input names | original | camel case | fooBar | fooBars |
React array props | original | camel case | fooBarList | fooBarsList |
React object props | original | camel case | fooBar | fooBars |
URL pathnames | any | kebab case | /foo-bar | /foo-bars |
Query string parameters | original | camel case | ?fooBar=baz | ?fooBars=baz |
UI table and column names | any | capital case | Foo Bar | Foo 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.