Step 3: Add Resolver
Add a closeProject mutation that cancels incomplete tasks and closes a project in a transaction. Also adds an admin machine user and Kysely type generator.
Configuration Files
Changes from Step 2: added admin machine user, resolver namespace, and generators export.
Since the
adminmachine user usesrole: "ADMIN", updatesrc/db/user.tsto include it:db.enum(["MANAGER", "STAFF", "ADMIN"]).
typescript
import { defineAuth, defineConfig, defineGenerators } from "@tailor-platform/sdk";
import { user } from "./src/db/user";
if (!process.env.WORKSPACE_ID) {
throw new Error("WORKSPACE_ID environment variable is not set");
}
export default defineConfig({
workspaceId: process.env.WORKSPACE_ID,
name: "project-management",
db: { "main-db": { files: [`./src/db/*.ts`] } },
auth: defineAuth("main-auth", {
userProfile: {
type: user,
usernameField: "email",
attributes: {
role: true,
},
},
machineUsers: {
manager: {
attributes: { role: "MANAGER" },
},
staff: {
attributes: { role: "STAFF" },
},
admin: {
attributes: { role: "ADMIN" },
},
},
}),
resolver: { "main-resolver": { files: [`./src/resolver/*.ts`] } },
});
export const generators = defineGenerators([
"@tailor-platform/kysely-type",
{ distPath: `./src/generated/tailordb.ts` },
]);For optional output fields, use
t.string({ optional: true }), nott.string().optional(). The.optional()chain does not exist.
typescript
import { createResolver, t } from "@tailor-platform/sdk";
import { getDB } from "../generated/tailordb";
export default createResolver({
name: "closeProject",
operation: "mutation",
input: {
id: t.string(),
},
body: async (context) => {
const db = getDB("main-db");
// 1. Fetch the project
const project = await db
.selectFrom("Project")
.selectAll()
.where("id", "=", context.input.id)
.executeTakeFirst();
if (!project) {
throw new Error(`Project not found, expected:1 got:0`);
}
if (project.status === "CLOSED") {
return { result: "Project is already closed." };
}
// 2. Get all incomplete tasks for the project
const incompleteTasks = await db
.selectFrom("Task")
.selectAll()
.where("projectId", "=", context.input.id)
.where("status", "!=", "DONE")
.execute();
// 3. Start a transaction
await db.transaction().execute(async (trx: any) => {
// Mark all incomplete tasks as canceled
for (const task of incompleteTasks) {
await trx
.updateTable("Task")
.set({ status: "CANCELED" })
.where("id", "=", task.id)
.execute();
}
// Close the project
await trx
.updateTable("Project")
.set({ status: "CLOSED" })
.where("id", "=", context.input.id)
.execute();
});
return {
result: `${context.input.id} project has been closed. All incomplete tasks are marked as canceled.`,
};
},
output: t.object({
result: t.string({ optional: true }),
}),
});File Tree
project-management/
├── package.json
├── tsconfig.json
├── tailor.config.ts
└── src/
├── common/permission.ts
├── db/
│ ├── user.ts # Updated: add ADMIN to role enum
│ ├── project.ts
│ └── task.ts
├── generated/
│ └── tailordb.ts # Auto-generated
└── resolver/
└── closeProject.ts # NewDeploy
You must run
generatebeforedeploywhenever you add or change database types.
bash
npm run generate
npm run deployVerify
graphql
mutation {
closeProject(input: { id: "project-uuid-here" }) {
result
}
}