🥰Sử dụng kết hợp prisma.io với wordpress như nào?

package.json

{
  "name": "wpnode",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon src/index.ts"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "description": "",
  "devDependencies": {
    "@types/express": "^5.0.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.2"
  },
  "dependencies": {
    "@prisma/client": "^6.4.1",
    "dotenv": "^16.4.7",
    "express": "^4.21.2",
    "mysql2": "^3.12.0",
    "nodemon": "^3.1.9",
    "prisma": "^6.4.1"
  }
}

.env

DATABASE_URL="mysql://root:@localhost:3306/wpclidemo"
PORT=3000

src\index.ts

import express, { urlencoded, json } from "express";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
const port = process.env.PORT || 8000;
const app = express();
app.use(urlencoded({ extended: true }));
app.use(json());
app.get("/", async (req, res) => {
  res.status(200).json({ msg: "Server is up and running" });
});
Object.defineProperty(BigInt.prototype, "toJSON", {
  get() {
    "use strict";
    return () => String(this);
  }
});
app.get('/posts', async (req, res) => {
  const posts = await prisma.wp_posts.findMany({
    where: { post_status: 'publish' },
    select: { ID: true, post_name: true, post_title: true, post_content: true },
    take: 5
  });
  res.json(posts);
});
app.listen(port, () => {
  console.log(`Server is listening at port ${port}`);
});

prisma\schema.prisma

generator client {
  provider = "prisma-client-js"
}
datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}
model wp_actionscheduler_actions {
  action_id            BigInt    @id @default(autoincrement()) @db.UnsignedBigInt
  hook                 String
  status               String    @db.VarChar(20)
  scheduled_date_gmt   DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  scheduled_date_local DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  priority             Int       @default(10) @db.UnsignedTinyInt
  args                 String?
  schedule             String?   @db.LongText
  group_id             BigInt    @default(0) @db.UnsignedBigInt
  attempts             Int       @default(0)
  last_attempt_gmt     DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  last_attempt_local   DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  claim_id             BigInt    @default(0) @db.UnsignedBigInt
  extended_args        String?   @db.VarChar(8000)
  @@index([args], map: "args")
  @@index([claim_id, status, scheduled_date_gmt], map: "claim_id_status_scheduled_date_gmt")
  @@index([group_id], map: "group_id")
  @@index([hook(length: 163), status, scheduled_date_gmt], map: "hook_status_scheduled_date_gmt")
  @@index([last_attempt_gmt], map: "last_attempt_gmt")
  @@index([scheduled_date_gmt], map: "scheduled_date_gmt")
  @@index([status, scheduled_date_gmt], map: "status_scheduled_date_gmt")
}
model wp_actionscheduler_claims {
  claim_id         BigInt    @id @default(autoincrement()) @db.UnsignedBigInt
  date_created_gmt DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  @@index([date_created_gmt], map: "date_created_gmt")
}
model wp_actionscheduler_groups {
  group_id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  slug     String @db.VarChar(255)
  @@index([slug(length: 191)], map: "slug")
}
model wp_actionscheduler_logs {
  log_id         BigInt    @id @default(autoincrement()) @db.UnsignedBigInt
  action_id      BigInt    @db.UnsignedBigInt
  message        String    @db.Text
  log_date_gmt   DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  log_date_local DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  @@index([action_id], map: "action_id")
  @@index([log_date_gmt], map: "log_date_gmt")
}
model wp_commentmeta {
  meta_id    BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  comment_id BigInt  @default(0) @db.UnsignedBigInt
  meta_key   String? @db.VarChar(255)
  meta_value String? @db.LongText
  @@index([comment_id], map: "comment_id")
  @@index([meta_key(length: 191)], map: "meta_key")
}
model wp_comments {
  comment_ID           BigInt   @id @default(autoincrement()) @db.UnsignedBigInt
  comment_post_ID      BigInt   @default(0) @db.UnsignedBigInt
  comment_author       String   @db.TinyText
  comment_author_email String   @default("") @db.VarChar(100)
  comment_author_url   String   @default("") @db.VarChar(200)
  comment_author_IP    String   @default("") @db.VarChar(100)
  comment_date         DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  comment_date_gmt     DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  comment_content      String   @db.Text
  comment_karma        Int      @default(0)
  comment_approved     String   @default("1") @db.VarChar(20)
  comment_agent        String   @default("") @db.VarChar(255)
  comment_type         String   @default("comment") @db.VarChar(20)
  comment_parent       BigInt   @default(0) @db.UnsignedBigInt
  user_id              BigInt   @default(0) @db.UnsignedBigInt
  @@index([comment_approved, comment_date_gmt], map: "comment_approved_date_gmt")
  @@index([comment_author_email(length: 10)], map: "comment_author_email")
  @@index([comment_date_gmt], map: "comment_date_gmt")
  @@index([comment_parent], map: "comment_parent")
  @@index([comment_post_ID], map: "comment_post_ID")
  @@index([comment_type], map: "woo_idx_comment_type")
}
model wp_links {
  link_id          BigInt   @id @default(autoincrement()) @db.UnsignedBigInt
  link_url         String   @default("") @db.VarChar(255)
  link_name        String   @default("") @db.VarChar(255)
  link_image       String   @default("") @db.VarChar(255)
  link_target      String   @default("") @db.VarChar(25)
  link_description String   @default("") @db.VarChar(255)
  link_visible     String   @default("Y") @db.VarChar(20)
  link_owner       BigInt   @default(1) @db.UnsignedBigInt
  link_rating      Int      @default(0)
  link_updated     DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  link_rel         String   @default("") @db.VarChar(255)
  link_notes       String   @db.MediumText
  link_rss         String   @default("") @db.VarChar(255)
  @@index([link_visible], map: "link_visible")
}
model wp_options {
  option_id    BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  option_name  String @unique(map: "option_name") @default("")
  option_value String @db.LongText
  autoload     String @default("yes") @db.VarChar(20)
  @@index([autoload], map: "autoload")
}
model wp_postmeta {
  meta_id    BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  post_id    BigInt  @default(0) @db.UnsignedBigInt
  meta_key   String? @db.VarChar(255)
  meta_value String? @db.LongText
  @@index([meta_key(length: 191)], map: "meta_key")
  @@index([post_id], map: "post_id")
}
model wp_posts {
  ID                    BigInt   @id @default(autoincrement()) @db.UnsignedBigInt
  post_author           BigInt   @default(0) @db.UnsignedBigInt
  post_date             DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  post_date_gmt         DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  post_content          String   @db.LongText
  post_title            String   @db.Text
  post_excerpt          String   @db.Text
  post_status           String   @default("publish") @db.VarChar(20)
  comment_status        String   @default("open") @db.VarChar(20)
  ping_status           String   @default("open") @db.VarChar(20)
  post_password         String   @default("") @db.VarChar(255)
  post_name             String   @default("") @db.VarChar(200)
  to_ping               String   @db.Text
  pinged                String   @db.Text
  post_modified         DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  post_modified_gmt     DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  post_content_filtered String   @db.LongText
  post_parent           BigInt   @default(0) @db.UnsignedBigInt
  guid                  String   @default("") @db.VarChar(255)
  menu_order            Int      @default(0)
  post_type             String   @default("post") @db.VarChar(20)
  post_mime_type        String   @default("") @db.VarChar(100)
  comment_count         BigInt   @default(0)
  @@index([post_author], map: "post_author")
  @@index([post_name(length: 191)], map: "post_name")
  @@index([post_parent], map: "post_parent")
  @@index([post_type, post_status, post_date, ID], map: "type_status_date")
}
model wp_term_relationships {
  object_id        BigInt @default(0) @db.UnsignedBigInt
  term_taxonomy_id BigInt @default(0) @db.UnsignedBigInt
  term_order       Int    @default(0)
  @@id([object_id, term_taxonomy_id])
  @@index([term_taxonomy_id], map: "term_taxonomy_id")
}
model wp_term_taxonomy {
  term_taxonomy_id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  term_id          BigInt @default(0) @db.UnsignedBigInt
  taxonomy         String @default("") @db.VarChar(32)
  description      String @db.LongText
  parent           BigInt @default(0) @db.UnsignedBigInt
  count            BigInt @default(0)
  @@unique([term_id, taxonomy], map: "term_id_taxonomy")
  @@index([taxonomy], map: "taxonomy")
}
model wp_termmeta {
  meta_id    BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  term_id    BigInt  @default(0) @db.UnsignedBigInt
  meta_key   String? @db.VarChar(255)
  meta_value String? @db.LongText
  @@index([meta_key(length: 191)], map: "meta_key")
  @@index([term_id], map: "term_id")
}
model wp_terms {
  term_id    BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  name       String @default("") @db.VarChar(200)
  slug       String @default("") @db.VarChar(200)
  term_group BigInt @default(0)
  @@index([name(length: 191)], map: "name")
  @@index([slug(length: 191)], map: "slug")
}
model wp_usermeta {
  umeta_id   BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  user_id    BigInt  @default(0) @db.UnsignedBigInt
  meta_key   String? @db.VarChar(255)
  meta_value String? @db.LongText
  @@index([meta_key(length: 191)], map: "meta_key")
  @@index([user_id], map: "user_id")
}
model wp_users {
  ID                  BigInt   @id @default(autoincrement()) @db.UnsignedBigInt
  user_login          String   @default("") @db.VarChar(60)
  user_pass           String   @default("") @db.VarChar(255)
  user_nicename       String   @default("") @db.VarChar(50)
  user_email          String   @default("") @db.VarChar(100)
  user_url            String   @default("") @db.VarChar(100)
  user_registered     DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  user_activation_key String   @default("") @db.VarChar(255)
  user_status         Int      @default(0)
  display_name        String   @default("") @db.VarChar(250)
  @@index([user_email], map: "user_email")
  @@index([user_login], map: "user_login_key")
  @@index([user_nicename], map: "user_nicename")
}
model wp_wc_admin_note_actions {
  action_id     BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  note_id       BigInt  @db.UnsignedBigInt
  name          String  @db.VarChar(255)
  label         String  @db.VarChar(255)
  query         String  @db.LongText
  status        String  @db.VarChar(255)
  actioned_text String  @db.VarChar(255)
  nonce_action  String? @db.VarChar(255)
  nonce_name    String? @db.VarChar(255)
  @@index([note_id], map: "note_id")
}
model wp_wc_admin_notes {
  note_id       BigInt    @id @default(autoincrement()) @db.UnsignedBigInt
  name          String    @db.VarChar(255)
  type          String    @db.VarChar(20)
  locale        String    @db.VarChar(20)
  title         String    @db.LongText
  content       String    @db.LongText
  content_data  String?   @db.LongText
  status        String    @db.VarChar(200)
  source        String    @db.VarChar(200)
  date_created  DateTime  @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  date_reminder DateTime? @db.DateTime(0)
  is_snoozable  Boolean   @default(false)
  layout        String    @default("") @db.VarChar(20)
  image         String?   @db.VarChar(200)
  is_deleted    Boolean   @default(false)
  is_read       Boolean   @default(false)
  icon          String    @default("info") @db.VarChar(200)
}
model wp_wc_category_lookup {
  category_tree_id BigInt @db.UnsignedBigInt
  category_id      BigInt @db.UnsignedBigInt
  @@id([category_tree_id, category_id])
}
model wp_wc_customer_lookup {
  customer_id      BigInt    @id @default(autoincrement()) @db.UnsignedBigInt
  user_id          BigInt?   @unique(map: "user_id") @db.UnsignedBigInt
  username         String    @default("") @db.VarChar(60)
  first_name       String    @db.VarChar(255)
  last_name        String    @db.VarChar(255)
  email            String?   @db.VarChar(100)
  date_last_active DateTime? @db.Timestamp(0)
  date_registered  DateTime? @db.Timestamp(0)
  country          String    @default("") @db.Char(2)
  postcode         String    @default("") @db.VarChar(20)
  city             String    @default("") @db.VarChar(100)
  state            String    @default("") @db.VarChar(100)
  @@index([email], map: "email")
}
model wp_wc_download_log {
  download_log_id BigInt   @id @default(autoincrement()) @db.UnsignedBigInt
  timestamp       DateTime @db.DateTime(0)
  permission_id   BigInt   @db.UnsignedBigInt
  user_id         BigInt?  @db.UnsignedBigInt
  user_ip_address String?  @default("") @db.VarChar(100)
  @@index([permission_id], map: "permission_id")
  @@index([timestamp], map: "timestamp")
}
model wp_wc_order_addresses {
  id           BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  order_id     BigInt  @db.UnsignedBigInt
  address_type String? @db.VarChar(20)
  first_name   String? @db.Text
  last_name    String? @db.Text
  company      String? @db.Text
  address_1    String? @db.Text
  address_2    String? @db.Text
  city         String? @db.Text
  state        String? @db.Text
  postcode     String? @db.Text
  country      String? @db.Text
  email        String? @db.VarChar(320)
  phone        String? @db.VarChar(100)
  @@unique([address_type, order_id], map: "address_type_order_id")
  @@index([email(length: 191)], map: "email")
  @@index([order_id], map: "order_id")
  @@index([phone], map: "phone")
}
model wp_wc_order_coupon_lookup {
  order_id        BigInt   @db.UnsignedBigInt
  coupon_id       BigInt
  date_created    DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  discount_amount Float    @default(0)
  @@id([order_id, coupon_id])
  @@index([coupon_id], map: "coupon_id")
  @@index([date_created], map: "date_created")
}
model wp_wc_order_operational_data {
  id                          BigInt    @id @default(autoincrement()) @db.UnsignedBigInt
  order_id                    BigInt?   @unique(map: "order_id") @db.UnsignedBigInt
  created_via                 String?   @db.VarChar(100)
  woocommerce_version         String?   @db.VarChar(20)
  prices_include_tax          Boolean?
  coupon_usages_are_counted   Boolean?
  download_permission_granted Boolean?
  cart_hash                   String?   @db.VarChar(100)
  new_order_email_sent        Boolean?
  order_key                   String?   @db.VarChar(100)
  order_stock_reduced         Boolean?
  date_paid_gmt               DateTime? @db.DateTime(0)
  date_completed_gmt          DateTime? @db.DateTime(0)
  shipping_tax_amount         Decimal?  @db.Decimal(26, 8)
  shipping_total_amount       Decimal?  @db.Decimal(26, 8)
  discount_tax_amount         Decimal?  @db.Decimal(26, 8)
  discount_total_amount       Decimal?  @db.Decimal(26, 8)
  recorded_sales              Boolean?
  @@index([order_key], map: "order_key")
}
model wp_wc_order_product_lookup {
  order_item_id         BigInt   @id @db.UnsignedBigInt
  order_id              BigInt   @db.UnsignedBigInt
  product_id            BigInt   @db.UnsignedBigInt
  variation_id          BigInt   @db.UnsignedBigInt
  customer_id           BigInt?  @db.UnsignedBigInt
  date_created          DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  product_qty           Int
  product_net_revenue   Float    @default(0)
  product_gross_revenue Float    @default(0)
  coupon_amount         Float    @default(0)
  tax_amount            Float    @default(0)
  shipping_amount       Float    @default(0)
  shipping_tax_amount   Float    @default(0)
  @@index([customer_id], map: "customer_id")
  @@index([date_created], map: "date_created")
  @@index([order_id], map: "order_id")
  @@index([product_id], map: "product_id")
}
model wp_wc_order_stats {
  order_id           BigInt    @id @db.UnsignedBigInt
  parent_id          BigInt    @default(0) @db.UnsignedBigInt
  date_created       DateTime  @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  date_created_gmt   DateTime  @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  date_paid          DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  date_completed     DateTime? @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  num_items_sold     Int       @default(0)
  total_sales        Float     @default(0)
  tax_total          Float     @default(0)
  shipping_total     Float     @default(0)
  net_total          Float     @default(0)
  returning_customer Boolean?
  status             String    @db.VarChar(200)
  customer_id        BigInt    @db.UnsignedBigInt
  @@index([customer_id], map: "customer_id")
  @@index([date_created], map: "date_created")
  @@index([status(length: 191)], map: "status")
}
model wp_wc_order_tax_lookup {
  order_id     BigInt   @db.UnsignedBigInt
  tax_rate_id  BigInt   @db.UnsignedBigInt
  date_created DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  shipping_tax Float    @default(0)
  order_tax    Float    @default(0)
  total_tax    Float    @default(0)
  @@id([order_id, tax_rate_id])
  @@index([date_created], map: "date_created")
  @@index([tax_rate_id], map: "tax_rate_id")
}
model wp_wc_orders {
  id                   BigInt    @id @db.UnsignedBigInt
  status               String?   @db.VarChar(20)
  currency             String?   @db.VarChar(10)
  type                 String?   @db.VarChar(20)
  tax_amount           Decimal?  @db.Decimal(26, 8)
  total_amount         Decimal?  @db.Decimal(26, 8)
  customer_id          BigInt?   @db.UnsignedBigInt
  billing_email        String?   @db.VarChar(320)
  date_created_gmt     DateTime? @db.DateTime(0)
  date_updated_gmt     DateTime? @db.DateTime(0)
  parent_order_id      BigInt?   @db.UnsignedBigInt
  payment_method       String?   @db.VarChar(100)
  payment_method_title String?   @db.Text
  transaction_id       String?   @db.VarChar(100)
  ip_address           String?   @db.VarChar(100)
  user_agent           String?   @db.Text
  customer_note        String?   @db.Text
  @@index([billing_email(length: 191)], map: "billing_email")
  @@index([customer_id, billing_email(length: 171)], map: "customer_id_billing_email")
  @@index([date_created_gmt], map: "date_created")
  @@index([date_updated_gmt], map: "date_updated")
  @@index([parent_order_id], map: "parent_order_id")
  @@index([status], map: "status")
  @@index([type, status, date_created_gmt], map: "type_status_date")
}
model wp_wc_orders_meta {
  id         BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  order_id   BigInt? @db.UnsignedBigInt
  meta_key   String? @db.VarChar(255)
  meta_value String? @db.Text
  @@index([meta_key(length: 100), meta_value(length: 82)], map: "meta_key_value")
  @@index([order_id, meta_key(length: 100), meta_value(length: 82)], map: "order_id_meta_key_meta_value")
}
model wp_wc_product_attributes_lookup {
  product_id             BigInt
  product_or_parent_id   BigInt
  taxonomy               String  @db.VarChar(32)
  term_id                BigInt
  is_variation_attribute Boolean
  in_stock               Boolean
  @@id([product_or_parent_id, term_id, product_id, taxonomy])
  @@index([is_variation_attribute, term_id], map: "is_variation_attribute_term_id")
}
model wp_wc_product_download_directories {
  url_id  BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  url     String  @db.VarChar(256)
  enabled Boolean @default(false)
  @@index([url(length: 191)], map: "url")
}
model wp_wc_product_meta_lookup {
  product_id       BigInt   @id
  sku              String?  @default("") @db.VarChar(100)
  global_unique_id String?  @default("") @db.VarChar(100)
  virtual          Boolean? @default(false)
  downloadable     Boolean? @default(false)
  min_price        Decimal? @db.Decimal(19, 4)
  max_price        Decimal? @db.Decimal(19, 4)
  onsale           Boolean? @default(false)
  stock_quantity   Float?
  stock_status     String?  @default("instock") @db.VarChar(100)
  rating_count     BigInt?  @default(0)
  average_rating   Decimal? @default(0.00) @db.Decimal(3, 2)
  total_sales      BigInt?  @default(0)
  tax_status       String?  @default("taxable") @db.VarChar(100)
  tax_class        String?  @default("") @db.VarChar(100)
  @@index([downloadable], map: "downloadable")
  @@index([min_price, max_price], map: "min_max_price")
  @@index([onsale], map: "onsale")
  @@index([sku(length: 50)], map: "sku")
  @@index([stock_quantity], map: "stock_quantity")
  @@index([stock_status], map: "stock_status")
  @@index([virtual], map: "virtual")
}
model wp_wc_rate_limits {
  rate_limit_id        BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  rate_limit_key       String @unique(map: "rate_limit_key", length: 191) @db.VarChar(200)
  rate_limit_expiry    BigInt @db.UnsignedBigInt
  rate_limit_remaining Int    @default(0) @db.SmallInt
}
model wp_wc_reserved_stock {
  order_id       BigInt
  product_id     BigInt
  stock_quantity Float    @default(0)
  timestamp      DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  expires        DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  @@id([order_id, product_id])
}
model wp_wc_tax_rate_classes {
  tax_rate_class_id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  name              String @default("") @db.VarChar(200)
  slug              String @unique(map: "slug", length: 191) @default("") @db.VarChar(200)
}
model wp_wc_webhooks {
  webhook_id        BigInt   @id @default(autoincrement()) @db.UnsignedBigInt
  status            String   @db.VarChar(200)
  name              String   @db.Text
  user_id           BigInt   @db.UnsignedBigInt
  delivery_url      String   @db.Text
  secret            String   @db.Text
  topic             String   @db.VarChar(200)
  date_created      DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  date_created_gmt  DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  date_modified     DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  date_modified_gmt DateTime @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  api_version       Int      @db.SmallInt
  failure_count     Int      @default(0) @db.SmallInt
  pending_delivery  Boolean  @default(false)
  @@index([user_id], map: "user_id")
}
model wp_woocommerce_api_keys {
  key_id          BigInt    @id @default(autoincrement()) @db.UnsignedBigInt
  user_id         BigInt    @db.UnsignedBigInt
  description     String?   @db.VarChar(200)
  permissions     String    @db.VarChar(10)
  consumer_key    String    @db.Char(64)
  consumer_secret String    @db.Char(43)
  nonces          String?   @db.LongText
  truncated_key   String    @db.Char(7)
  last_access     DateTime? @db.DateTime(0)
  @@index([consumer_key], map: "consumer_key")
  @@index([consumer_secret], map: "consumer_secret")
}
model wp_woocommerce_attribute_taxonomies {
  attribute_id      BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  attribute_name    String  @db.VarChar(200)
  attribute_label   String? @db.VarChar(200)
  attribute_type    String  @db.VarChar(20)
  attribute_orderby String  @db.VarChar(20)
  attribute_public  Int     @default(1)
  @@index([attribute_name(length: 20)], map: "attribute_name")
}
model wp_woocommerce_downloadable_product_permissions {
  permission_id       BigInt    @id @default(autoincrement()) @db.UnsignedBigInt
  download_id         String    @db.VarChar(36)
  product_id          BigInt    @db.UnsignedBigInt
  order_id            BigInt    @default(0) @db.UnsignedBigInt
  order_key           String    @db.VarChar(200)
  user_email          String    @db.VarChar(200)
  user_id             BigInt?   @db.UnsignedBigInt
  downloads_remaining String?   @db.VarChar(9)
  access_granted      DateTime  @default(dbgenerated("('0000-00-00 00:00:00')")) @db.DateTime(0)
  access_expires      DateTime? @db.DateTime(0)
  download_count      BigInt    @default(0) @db.UnsignedBigInt
  @@index([product_id, order_id, order_key(length: 16), download_id], map: "download_order_key_product")
  @@index([download_id, order_id, product_id], map: "download_order_product")
  @@index([order_id], map: "order_id")
  @@index([user_id, order_id, downloads_remaining, access_expires], map: "user_order_remaining_expires")
}
model wp_woocommerce_log {
  log_id    BigInt   @id @default(autoincrement()) @db.UnsignedBigInt
  timestamp DateTime @db.DateTime(0)
  level     Int      @db.SmallInt
  source    String   @db.VarChar(200)
  message   String   @db.LongText
  context   String?  @db.LongText
  @@index([level], map: "level")
}
model wp_woocommerce_order_itemmeta {
  meta_id       BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  order_item_id BigInt  @db.UnsignedBigInt
  meta_key      String? @db.VarChar(255)
  meta_value    String? @db.LongText
  @@index([meta_key(length: 32)], map: "meta_key")
  @@index([order_item_id], map: "order_item_id")
}
model wp_woocommerce_order_items {
  order_item_id   BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  order_item_name String @db.Text
  order_item_type String @default("") @db.VarChar(200)
  order_id        BigInt @db.UnsignedBigInt
  @@index([order_id], map: "order_id")
}
model wp_woocommerce_payment_tokenmeta {
  meta_id          BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  payment_token_id BigInt  @db.UnsignedBigInt
  meta_key         String? @db.VarChar(255)
  meta_value       String? @db.LongText
  @@index([meta_key(length: 32)], map: "meta_key")
  @@index([payment_token_id], map: "payment_token_id")
}
model wp_woocommerce_payment_tokens {
  token_id   BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  gateway_id String  @db.VarChar(200)
  token      String  @db.Text
  user_id    BigInt  @default(0) @db.UnsignedBigInt
  type       String  @db.VarChar(200)
  is_default Boolean @default(false)
  @@index([user_id], map: "user_id")
}
model wp_woocommerce_sessions {
  session_id     BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  session_key    String @unique(map: "session_key") @db.Char(32)
  session_value  String @db.LongText
  session_expiry BigInt @db.UnsignedBigInt
}
model wp_woocommerce_shipping_zone_locations {
  location_id   BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  zone_id       BigInt @db.UnsignedBigInt
  location_code String @db.VarChar(200)
  location_type String @db.VarChar(40)
  @@index([location_type(length: 10), location_code(length: 20)], map: "location_type_code")
  @@index([zone_id], map: "zone_id")
}
model wp_woocommerce_shipping_zone_methods {
  zone_id      BigInt  @db.UnsignedBigInt
  instance_id  BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  method_id    String  @db.VarChar(200)
  method_order BigInt  @db.UnsignedBigInt
  is_enabled   Boolean @default(true)
}
model wp_woocommerce_shipping_zones {
  zone_id    BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  zone_name  String @db.VarChar(200)
  zone_order BigInt @db.UnsignedBigInt
}
model wp_woocommerce_tax_rate_locations {
  location_id   BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  location_code String @db.VarChar(200)
  tax_rate_id   BigInt @db.UnsignedBigInt
  location_type String @db.VarChar(40)
  @@index([location_type(length: 10), location_code(length: 20)], map: "location_type_code")
  @@index([tax_rate_id], map: "tax_rate_id")
}
model wp_woocommerce_tax_rates {
  tax_rate_id       BigInt @id @default(autoincrement()) @db.UnsignedBigInt
  tax_rate_country  String @default("") @db.VarChar(2)
  tax_rate_state    String @default("") @db.VarChar(200)
  tax_rate          String @default("") @db.VarChar(8)
  tax_rate_name     String @default("") @db.VarChar(200)
  tax_rate_priority BigInt @db.UnsignedBigInt
  tax_rate_compound Int    @default(0)
  tax_rate_shipping Int    @default(1)
  tax_rate_order    BigInt @db.UnsignedBigInt
  tax_rate_class    String @default("") @db.VarChar(200)
  @@index([tax_rate_class(length: 10)], map: "tax_rate_class")
  @@index([tax_rate_country], map: "tax_rate_country")
  @@index([tax_rate_priority], map: "tax_rate_priority")
  @@index([tax_rate_state(length: 2)], map: "tax_rate_state")
}
model wp_wpsmtp_logs {
  mail_id   Int      @id @default(autoincrement())
  timestamp DateTime @default(now()) @db.Timestamp(0)
  to        String   @default("0") @db.VarChar(200)
  subject   String   @default("0") @db.VarChar(200)
  message   String?  @db.Text
  headers   String?  @db.Text
  error     String?  @db.Text
}
model wp_yoast_indexable {
  id                             Int       @id @default(autoincrement()) @db.UnsignedInt
  permalink                      String?   @db.LongText
  permalink_hash                 String?   @db.VarChar(40)
  object_id                      BigInt?
  object_type                    String    @db.VarChar(32)
  object_sub_type                String?   @db.VarChar(32)
  author_id                      BigInt?
  post_parent                    BigInt?
  title                          String?   @db.Text
  description                    String?   @db.MediumText
  breadcrumb_title               String?   @db.Text
  post_status                    String?   @db.VarChar(20)
  is_public                      Boolean?
  is_protected                   Boolean?  @default(false)
  has_public_posts               Boolean?
  number_of_pages                Int?      @db.UnsignedInt
  canonical                      String?   @db.LongText
  primary_focus_keyword          String?
  primary_focus_keyword_score    Int?
  readability_score              Int?
  is_cornerstone                 Boolean?  @default(false)
  is_robots_noindex              Boolean?  @default(false)
  is_robots_nofollow             Boolean?  @default(false)
  is_robots_noarchive            Boolean?  @default(false)
  is_robots_noimageindex         Boolean?  @default(false)
  is_robots_nosnippet            Boolean?  @default(false)
  twitter_title                  String?   @db.Text
  twitter_image                  String?   @db.LongText
  twitter_description            String?   @db.LongText
  twitter_image_id               String?
  twitter_image_source           String?   @db.Text
  open_graph_title               String?   @db.Text
  open_graph_description         String?   @db.LongText
  open_graph_image               String?   @db.LongText
  open_graph_image_id            String?
  open_graph_image_source        String?   @db.Text
  open_graph_image_meta          String?   @db.MediumText
  link_count                     Int?
  incoming_link_count            Int?
  prominent_words_version        Int?      @db.UnsignedInt
  created_at                     DateTime? @db.DateTime(0)
  updated_at                     DateTime  @default(now()) @db.Timestamp(0)
  blog_id                        BigInt    @default(1)
  language                       String?   @db.VarChar(32)
  region                         String?   @db.VarChar(32)
  schema_page_type               String?   @db.VarChar(64)
  schema_article_type            String?   @db.VarChar(64)
  has_ancestors                  Boolean?  @default(false)
  estimated_reading_time_minutes Int?
  version                        Int?      @default(1)
  object_last_modified           DateTime? @db.DateTime(0)
  object_published_at            DateTime? @db.DateTime(0)
  inclusive_language_score       Int?
  @@index([object_id, object_type], map: "object_id_and_type")
  @@index([object_type, object_sub_type], map: "object_type_and_sub_type")
  @@index([permalink_hash, object_type], map: "permalink_hash_and_object_type")
  @@index([prominent_words_version, object_type, object_sub_type, post_status], map: "prominent_words")
  @@index([object_published_at, is_robots_noindex, object_type, object_sub_type], map: "published_sitemap_index")
  @@index([post_parent, object_type, post_status, object_id], map: "subpages")
}
model wp_yoast_indexable_hierarchy {
  indexable_id Int    @db.UnsignedInt
  ancestor_id  Int    @db.UnsignedInt
  depth        Int?   @db.UnsignedInt
  blog_id      BigInt @default(1)
  @@id([indexable_id, ancestor_id])
  @@index([ancestor_id], map: "ancestor_id")
  @@index([depth], map: "depth")
  @@index([indexable_id], map: "indexable_id")
}
model wp_yoast_migrations {
  id      Int     @id @default(autoincrement()) @db.UnsignedInt
  version String? @unique(map: "wp_yoast_migrations_version")
}
model wp_yoast_primary_term {
  id         Int       @id @default(autoincrement()) @db.UnsignedInt
  post_id    BigInt?
  term_id    BigInt?
  taxonomy   String    @db.VarChar(32)
  created_at DateTime? @db.DateTime(0)
  updated_at DateTime  @default(now()) @db.Timestamp(0)
  blog_id    BigInt    @default(1)
  @@index([post_id, taxonomy], map: "post_taxonomy")
  @@index([post_id, term_id], map: "post_term")
}
model wp_yoast_seo_links {
  id                  BigInt  @id @default(autoincrement()) @db.UnsignedBigInt
  url                 String? @db.VarChar(255)
  post_id             BigInt? @db.UnsignedBigInt
  target_post_id      BigInt? @db.UnsignedBigInt
  type                String? @db.VarChar(8)
  indexable_id        Int?    @db.UnsignedInt
  target_indexable_id Int?    @db.UnsignedInt
  height              Int?    @db.UnsignedInt
  width               Int?    @db.UnsignedInt
  size                Int?    @db.UnsignedInt
  language            String? @db.VarChar(32)
  region              String? @db.VarChar(32)
  @@index([indexable_id, type], map: "indexable_link_direction")
  @@index([post_id, type], map: "link_direction")
}

Kết hợp Prisma.io với WordPress có thể giúp bạn sử dụng cơ sở dữ liệu của WordPress một cách linh hoạt hơn trong các ứng dụng Node.js. Dưới đây là hướng dẫn tổng quan về cách tích hợp hai công nghệ này:

🚀 1. Tại sao sử dụng Prisma với WordPress?

  • WordPress sử dụng MySQL làm cơ sở dữ liệu, trong khi Prisma là ORM hiện đại giúp truy vấn MySQL dễ dàng hơn.

  • Nếu bạn muốn xây dựng một API Node.js để đọc/ghi dữ liệu vào WordPress mà không cần dùng các API REST/XML-RPC của WordPress.

  • Tận dụng TypeScript, migrations, và caching của Prisma thay vì viết SQL thuần.

🛠 2. Cài đặt Prisma trong dự án Node.js

Trước tiên, bạn cần có một dự án Node.js. Nếu chưa có, hãy tạo một dự án mới:

mkdir wordpress-prisma && cd wordpress-prisma
npm init -y

Tiếp theo, cài đặt Prisma và driver MySQL:

npm install prisma @prisma/client mysql2
npx prisma init

3. Cấu hình kết nối MySQL của WordPress

Mở file .env và cập nhật thông tin kết nối MySQL của WordPress:

DATABASE_URL="mysql://root:password@localhost:3306/wordpress"

Trong đó:

  • root là user MySQL.

  • password là mật khẩu MySQL.

  • wordpress là tên database của WordPress.

🏗 4. Khởi tạo Prisma Schema từ Database WordPress

WordPress có sẵn bảng như wp_posts, wp_users, wp_comments... Bạn có thể import schema từ database:

npx prisma db pull

Lệnh này sẽ tạo file prisma/schema.prisma với cấu trúc bảng có sẵn từ WordPress.

Mở file prisma/schema.prisma, bạn sẽ thấy các model tương ứng, ví dụ:

model wp_posts {
  ID         Int      @id @default(autoincrement())
  post_title String
  post_content String
  post_status String
  post_author Int
  post_date DateTime
}

Sau đó, chạy lệnh sau để cập nhật Prisma Client:

npx prisma generate

5. Viết API Node.js để lấy dữ liệu từ WordPress

Tạo file index.js để truy vấn dữ liệu từ WordPress:

const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function main() {
  // Lấy danh sách bài viết
  const posts = await prisma.wp_posts.findMany({
    where: { post_status: 'publish' },
    select: { ID: true, post_title: true, post_content: true },
  });

  console.log(posts);
}

main()
  .catch((e) => console.error(e))
  .finally(async () => {
    await prisma.$disconnect();
  });

Chạy thử:

node index.js

Nếu thành công, bạn sẽ thấy danh sách bài viết từ WordPress.

🔄 6. Tạo API REST với Express.js

Bạn có thể tạo một API đơn giản với Express để truy vấn dữ liệu từ WordPress:

npm install express

Tạo file server.js:

const express = require('express');
const { PrismaClient } = require('@prisma/client');

const app = express();
const prisma = new PrismaClient();

app.get('/posts', async (req, res) => {
  const posts = await prisma.wp_posts.findMany({
    where: { post_status: 'publish' },
    select: { ID: true, post_title: true, post_content: true },
  });
  res.json(posts);
});

app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

Chạy server:

node server.js

Mở trình duyệt và truy cập http://localhost:3000/posts để xem danh sách bài viết.

7. Những lưu ý khi dùng Prisma với WordPress

  • WordPress có bảng mặc định, nếu bạn cần mở rộng thì nên tạo bảng riêng để tránh xung đột với hệ thống.

  • Không nên chỉnh sửa cấu trúc bảng mặc định của WordPress nếu bạn vẫn sử dụng hệ thống WordPress truyền thống.

  • Sử dụng caching như Redis hoặc GraphQL để tối ưu truy vấn nếu cần.

📌 Tổng kết

  • Dùng Prisma để kết nối MySQL của WordPress trong ứng dụng Node.js.

  • Import schema từ database bằng prisma db pull.

  • Viết API với Express để truy vấn bài viết từ WordPress.

Bạn có đang muốn xây dựng dự án cụ thể nào không? 🚀

Last updated