Configure Prisma in a Remix Application

Jun 30 2022

5 min read

#Create a Remix Project

Remix is a full stack web framework built on top of React that lets you focus on user interface work back through web standards to deliver a fast, slick, and resilient user experience.

To initialize a new Remix project, run the below command.


npx create-remix@latest <project-name>
? Do you want me to run `npm install`? Yes

This will create a basic project structure with typescript and a single index route. To start the dev server, run the below command.


npm run dev


#Installing Prisma ORM

Prisma in one of the leading ORM's for typescript and node.js projects. It provides a CLI tool to install packages, create migrations, and client code.


npm i -D prisma

#Initialize Prisma

With the CLI installed, we can initialize prisma with npx prisma init command by passing the --datasource-provider flag.


npx prisma init --datasource-provider sqlite

Prisma supports both SQL and NO-SQL databases. Following are the list of databases supported by Prisma.

  • PostgreSQL
  • MySQL
  • SQLite
  • Microsoft SQL Server
  • MongoDB

After initialization, a new folder named prisma and a .env file is create in the root directory. Inside it, a schema file named schema.prisma is created.

schema.prisma
Copy

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}

.env
Copy

DATABASE_URL="file:./dev.db"

#Define a Post model

The schema file is where we define all of our models and relations if any. Prisma has its own syntax for defining any models and their data types. Let's create a Post model with built-in and user defined fields.

schema.prisma
Copy

model Post {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

Here, we defined a Post model with three built-in fields which Prisma creates and manages for any record. We can add our own custom fields as well with their appropriate types.

schema.prisma
Copy

model Post {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String?
body String
}

We added a title and body custom fields which are String. We can also make any field optional using ? at the end of the field type.

#Create the database

Now we have the new model in place, we haven't told Prisma to create a database with this schema. To create a database, execute the following command. This will create a db file inside the prisma folder.


npx prisma db push

#Add data using Prisma Studio

You can add data directly to the created db using a GUI tool provided by Prisma. Run the below command to open a UI in browser and add the title and body fields.


npx prisma studio


#Installing Prisma Client

Prisma provides a separate client package to interact with the database. We can do all the CRUD operations with minimal setup and it also provides a fully type-safe API to avoid mistakes. Run below command to install the client.


npm i @prisma/client

#Creating global db connection

Remix follows naming conventions for files where any file with name .server will be executed in server side. Let's create a database service which creates a global connection and shared across all the application.

services/db.server.ts
Copy

import { PrismaClient } from "@prisma/client";
let db: PrismaClient
declare global {
var __db: PrismaClient | undefined
}
// this is needed because in development we don't want to restart
// the server with every change, but we want to make sure we don't
// create a new connection to the DB with every change either.
if(process.env.NODE_ENV === 'production') {
db = new PrismaClient();
} else {
if(!global.__db) {
global.__db = new PrismaClient();
}
db = global.__db
}
export { db };

#Creating post service

Create a new file named post.server.ts under services where we will write the logic to read and write the data.

services/post.server.ts
Copy

import { db } from "~/services/db.server";
export type { Post } from "@prisma/client";
export const getPosts = () => db.post.findMany();

We are exporting a function which will give all the posts and also a type Post so that in client side, while importing, we can import everything from this file instead of prisma libraries.

#Reading data in UI

To get all the posts in UI, we need a loader function which is specific to Remix. The loader function will always run in the server and gives back the result in json format. Then, we need a hook called useLoaderData inside the component function to get the result from the loader function.

routes/index.tsx
Copy

import type { LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import type { Post } from "~/services/post.server";
import { getPosts } from "~/services/post.server";
type LoaderData = {
posts: Post[];
};
export const loader: LoaderFunction = async () => {
const data: LoaderData = { posts: await getPosts() };
return json(data);
};
export default function Index() {
const { posts } = useLoaderData<LoaderData>();
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1" }}>
<h1>Welcome to Remix</h1>
<ul>
{posts.map((post) => (
<li key={post.title}>
<div>
<h2>{post.title}</h2>
<p>{post.body}</p>
</div>
</li>
))}
</ul>
</div>
);
}


#Summary

Remix is a runtime environment for both server and client, built on top of React and provides awesome DX with features like SSR, session management, routing etc.

Prisma is one of the best ORM's for typescript and node.js projects. It provides a fully type-safe API and a CLI to generate code, migrations, etc.