Setup Ents in an existing Convex project
Starting a new project from scratch? Follow the setup guide instead.
Use this guide if you have a lot of existing code using ctx.db
which you want
to keep while you migrate to Ents.
Install the NPM library
npm install convex-ents convex-helpers
This also installs convex-helpers
, used in step 3.
Modify your schema
Update your schema file. These changes will have no effect on your existing code
using ctx.db
.
- Replace
defineSchema
withdefineEntSchema
- Replace all
defineTable
withdefineEnt
- Add
entDefinitions
as shown
Example changes:
- import { defineSchema, defineTable } from "convex/server";
+ import { defineEnt, defineEntSchema, getEntDefinitions } from "convex-ents";
import { v } from "convex/values";
- const schema = defineSchema({
+ const schema = defineEntSchema({
- messages: defineTable({
+ messages: defineEnt({
text: v.string(),
}),
- users: defineTable({
+ users: defineEnt({
name: v.string(),
}),
});
export default schema;
+ export const entDefinitions = getEntDefinitions(schema);
For more details on declaring the schema see Ent Schema.
Setup custom functions
Add a functions.ts
file with the following contents:
Click to show
import { entsTableFactory } from "convex-ents";
import {
customAction,
customCtx,
customMutation,
customQuery,
} from "convex-helpers/server/customFunctions";
import { GenericDatabaseReader, GenericDatabaseWriter } from "convex/server";
import { DataModel } from "./_generated/dataModel";
import {
internalMutation as baseInternalMutation,
internalQuery as baseInternalQuery,
mutation as baseMutation,
query as baseQuery,
} from "./_generated/server";
import { entDefinitions } from "./schema";
type LegacyTables = "messages" | "users";
export const query = customQuery(
baseQuery,
customCtx(async (ctx) => {
return {
table: entsTableFactory(ctx, entDefinitions),
db: ctx.db as unknown as GenericDatabaseReader<
Pick<DataModel, LegacyTables>
>,
};
}),
);
export const internalQuery = customQuery(
baseInternalQuery,
customCtx(async (ctx) => {
return {
table: entsTableFactory(ctx, entDefinitions),
db: ctx.db as unknown as GenericDatabaseReader<
Pick<DataModel, LegacyTables>
>,
};
}),
);
export const mutation = customMutation(
baseMutation,
customCtx(async (ctx) => {
return {
table: entsTableFactory(ctx, entDefinitions),
db: ctx.db as GenericDatabaseWriter<Pick<DataModel, LegacyTables>>,
};
}),
);
export const internalMutation = customMutation(
baseInternalMutation,
customCtx(async (ctx) => {
return {
table: entsTableFactory(ctx, entDefinitions),
db: ctx.db as GenericDatabaseWriter<Pick<DataModel, LegacyTables>>,
};
}),
);
Update the LegacyTables
type with an enumeration of your existing tables which
you want to keep using with ctx.db
.
Add helper types
Add a types.ts
file with the following contents:
Click to show
import { GenericEnt, GenericEntWriter } from "convex-ents";
import { CustomCtx } from "convex-helpers/server/customFunctions";
import { TableNames } from "./_generated/dataModel";
import { mutation, query } from "./functions";
import { entDefinitions } from "./schema";
export type QueryCtx = CustomCtx<typeof query>;
export type MutationCtx = CustomCtx<typeof mutation>;
export type Ent<TableName extends TableNames> = GenericEnt<
typeof entDefinitions,
TableName
>;
export type EntWriter<TableName extends TableNames> = GenericEntWriter<
typeof entDefinitions,
TableName
>;
Use custom functions to read and write ents
You can now replace function constructors from _generated
with the custom ones
you just set up. This will have no impact on your existing functions:
import { v } from "convex/values";
- import { mutation, query } from "./_generated/server";
+ import { mutation, query } from "./functions";
export const list = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("messages");
+ // You can now use ctx.table as well:
+ return await ctx.table("messages");
},
});
Similarly replace QueryCtx
and MutationCtx
with imports from ./types
.
You can now use ctx.table
for new code and to replace existing code.
For more details see Reading Ents and Writing Ents.
Adopt Ents features
Convex Ents maintain invariants, such as enforcing unique fields, leaving no
dangling references, etc. To make sure these invariants are not violated, you
should not acccess a table with any Ents specific definitions (unique fields,
edges) via the built-in ctx.db
API.
To prevent ctx.db
access to some of the tables in your schema, amend your
functions.ts
file. In this example we remove access to "messages"
from
ctx.db
:
- type LegacyTables = "messages" | "users";
+ type LegacyTables = "users";
(optional) Finish moving to ctx.table
Once you're no longer using ctx.db
in any of your code, you can remove it from
your custom functions setup:
// For query and internalQuery:
+ db: undefined,
- db: ctx.db as unknown as GenericDatabaseReader<
- Pick<DataModel, LegacyTables>
- >,
// For mutation and internalMutation:
+ db: undefined,
- db: ctx.db as GenericDatabaseWriter<
- Pick<DataModel, LegacyTables>
- >,
For more details see Configuring Functions.