Other ORMs
Get Started

Introduction

Currently, FactoryJS does not provide plugins for all ORM libraries, but you can manually define factories to use with other ORMs.

Installation

You don't need to install any additional packages. Run the following command to install the @factory-js/factory package.

pnpm add -D @factory-js/factory

Defining a Simple Model

Let's define a simple model without relations. Here, we'll use an example with Drizzle ORM (opens in a new tab).

To define a factory, use .define. In the example below, we use Faker (opens in a new tab) to generate values, but you can use any library or fixed values.

factories/user-factory.ts
import { factory } from "@factory-js/factory";
import { faker } from "@faker-js/faker";
// `create` is a function that calls the Drizzle API to save data to the database
import { create } from "./utils/create";
 
export const userFactory = factory.define(
  {
    props: {
      name: () => faker.string.alphanumeric(40),
      email: () => faker.internet.exampleEmail(),
      role: () => faker.helpers.arrayElement(["guest", "admin"] as const),
    },
    vars: {},
  },
  // The second argument is a function to save the model
  (props) => create(users, props),
);

For a working sample code, check the example directory (opens in a new tab).

As an example of using the factory defined above, let's write a simple test. The following is a function that determines if a user is an admin. By calling .create, you can get the result saved to the database.

src/is-admin.test.ts
import { expect, it, describe } from "vitest";
import { userFactory } from "../factories/user-factory";
import { isAdmin } from "./is-admin";
 
describe("when a user is admin", () => {
  it("returns true", async () => {
    // To create an admin user, set the `role` to `admin` with `.props`
    const user = await userFactory.props({ role: () => "admin" }).create();
    expect(isAdmin(user)).toBe(true);
  });
});

Defining Relations

When defining models with one-to-one or one-to-many relationships, there are two ways to do it. Let's consider a model with a one-to-one relationship. The profile model must always be linked to the user model in a one-to-one relationship.

Method Without Using Variables

The first method is a simple one that doesn't use variables. Define the factories as in the previous example.

factories/index.ts
import { factory } from "@factory-js/factory";
import { faker } from "@faker-js/faker";
import { create } from "./utils/create";
 
// Define the user factory
export const userFactory = factory.define(
  {
    props: {
      name: () => faker.string.alphanumeric(40),
      email: () => faker.internet.exampleEmail(),
      role: () => faker.helpers.arrayElement(["guest", "admin"] as const),
    },
    vars: {},
  },
  (props) => create(users, props),
);
 
// Define the profile factory
export const profileFactory = factory.define(
  {
    props: {
      // By using `later`, an exception will be thrown
      // if `.props` to set `userId` later is forgotten
      userId: later<number>(),
      bio: () => faker.string.alphanumeric(40),
    },
    vars: {},
  },
  (props) => create(profiles, props),
);

When using these, you can specify the model ID with .props to link the relationships. In the example below, we create a user first, and then create a profile linked to that user ID.

src/get-profile.test.ts
import { expect, it, describe } from "vitest";
import { userFactory, profileFactory } from "../factories";
import { getProfile } from "./get-profile";
 
describe("when a user exists", () => {
  it("returns the user profile", async () => {
    const user = await userFactory.create();
    const profile = await profileFactory
      .props({ userId: () => user.id })
      .create();
 
    await expect(getProfile(user.id)).resolves.toStrictEqual({
      name: user.name,
      bio: profile.bio,
    });
  });
});

Method Using Variables

The second method uses variables. By using variables, you can set default related models, so you don't need to specify the relationships each time you use them.

factories/index.ts
import { factory } from "@factory-js/factory";
import { faker } from "@faker-js/faker";
import { create } from "./utils/create";
 
const userFactory = factory.define(
  {
    props: {
      name: () => faker.string.alphanumeric(40),
      email: () => faker.internet.exampleEmail(),
      role: () => faker.helpers.arrayElement(["guest", "admin"] as const),
    },
    vars: {},
  },
  (props) => create(users, props),
);
 
const profileFactory = factory
  .define(
    {
      props: {
        userId: later<number>(),
        bio: () => faker.string.alphanumeric(40),
      },
      vars: {
        // Store the default user in a variable
        user: () => userFactory.create(),
      },
    },
    (props) => create(profiles, props),
  )
  .props({
    // Use the variable to get the user's ID and set it to `userId`
    userId: async ({ vars }) => (await vars.user).id,
  });

For more details on how to use variables, refer to Variable.

This allows you to create the profile model without having to create the user model.

src/get-profile.test.ts
import { expect, it, describe } from "vitest";
import { userFactory, profileFactory } from "../factories";
import { isValidProfile } from "./is-valid-profile";
 
describe("when a profile is valid", () => {
  it("returns true", async () => {
    // Create a profile using the default user
    const profile = await profileFactory.create();
    await expect(isValidProfile(profile)).resolves.toBe(true);
  });
});

Of course, you can also change the relationships as needed using .vars. In the example below, we want to create a profile for an admin user, so we change the relationship using .vars.

describe("when a user is admin", () => {
  it("returns true", async () => {
    const user = await userFactory.props({ role: () => "admin" }).create();
    const profile = await profileFactory.vars({ user: () => user }).create();
 
    await expect(isAdminProfile(user.id)).resolves.toBe(true);
  });
});

Which method to use is a matter of preference, but if in doubt, it is recommended to use variables as it can reduce the amount of code.