관계형 데이터를 문서로 노출하는 매력적인 방법.
이 기사에서는 정규화 된 관계형 데이터 (1-n, m-n 카디널리티)를 데이터의 문서 기반 표현에 연결하기 위해 PostgreSQL에 연결된 GraphQL 및 Sequelize의 통합과 함께 제공되는 NodeJS 앱을 살펴볼 것입니다.
GraphQL 읽기 전용 쿼리 예제를 제공하고 Object Relational Mapper Sequelize를 통해 효과적인 SQL 문으로 변환되는 방법을 보여 드리겠습니다.
빠른 시작을 위해 이 문서에서는 이 GH 저장소에 대한 작업 예제를 제공합니다.
프로젝트 설정
NODE_ENV=development
PORT=9000
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=its-my-database
DB_USERNAME=its-me
DB_PASSWORD=its-my-password
DB_SCHEMA=its-my-schema
DB_DIALECT=postgres
? Sequelize ORM connected to postgres @ 127.0.0.1:5432
? Server ready at http://localhost:4000/
? Subscriptions ready at ws://localhost:4000/graphql
이 메시지가 표시되면 축하합니다. Sequelize ORM이 PostgreSQL 서버에 연결되어 있고 GraphQL 엔드 포인트가 로컬 컴퓨터에 노출됩니다.
개체 모델
이 모델은 정확히 하나의 조직 (일대 다 관계)에 속하고 여러 역할 (다 대다 관계)을 수행 할 수 있는 사용자를 제공합니다.
GraphQL 관점에서 스키마는 다음과 같이 설명됩니다. 각 GraphQL 스키마 파일에는 개체 정의 자체, 두 개의 입력 정의 (하나는 새 레코드 <Object> Input을 만들고 다른 하나는 개체 <Object> RecordInput의 정확히 하나의 레코드를 쿼리하기 위한 것) 및 출력이 포함되어 있음을 알 수 있습니다. 개체 및 추가 메타 데이터에 대한 하나 이상의 레코드를 전달할 수 있는 개체입니다. 나중에 GraphQL Playground에서 이러한 정의를 사용할 것입니다.
GraphQL 개체 : 조직
여러 사용자가 연관 될 수 있는 매우 간단한 조직 표현입니다.
type Org { | |
id: ID | |
name: String | |
} | |
input OrgInput { | |
id: ID | |
name: String | |
} | |
input OrgRecordInput { | |
id: ID | |
name: String | |
} | |
type OrgOutput { | |
count: Int | |
data: [Org] | |
} |
GraphQL 객체 : 역할
여러 사용자와 연결할 수 있는 역할을 보유하는 또 다른 간단한 개체입니다.
type Role { | |
id: ID | |
name: String | |
} | |
input RoleInput { | |
id: ID | |
name: String | |
} | |
input RoleRecordInput { | |
id: ID | |
} | |
type RoleOutput { | |
count: Int | |
data: [Role] | |
} |
GraphQL Object : User
사용자를 설명하기 위한 몇 가지 매우 기본적인 필드를 포함하고 조직 및 사용자와 관련된 여러 역할을 참조하는 사용자 개체입니다.
type User { | |
id: ID | |
serial: String | |
firstName: String | |
lastName: String | |
org: Org | |
roles: [Role] | |
} | |
input UserInput { | |
id: ID | |
serial: String | |
firstName: String | |
lastName: String | |
org: OrgRecordInput | |
} | |
input UserRecordInput { | |
id: ID | |
serial: String | |
} | |
type UserOutput { | |
count: Int | |
data: [User] | |
} |
또한 Sequelize에 대한 개체 모델을 설명해야 합니다. 이 모델을 사용하여 PostgreSQL의 스키마를 관리합니다. 처음에는 이 모델을 통해 필요한 데이터베이스 테이블을 만들고 데이터의 관계형 표현을 JSON으로 일치 시킬 것입니다. 음, 우리를 위해 이 작업을 수행하는 것은 모두 Sequelize입니다. Sequelize에 대한 자세한 내용은 다음을 참조하십시오.
Sequelize Object : Org
이름 기반 쿼리를 사용하여 데이터에 보다 효율적으로 액세스 할 수 있도록 ID가 UUID 필드이고 이름 필드가 고유 한 String (100) + 이름에 대한 추가 색인 인 테이블 조직을 만들 것입니다.
export default (sequelize, DataTypes) => { | |
const Org = sequelize.define('org', | |
{ | |
id: { | |
type: DataTypes.UUID, | |
primaryKey: true, | |
defaultValue: DataTypes.UUIDV4 | |
}, | |
name: { | |
type: DataTypes.STRING(100), | |
unique: true, | |
allowNull: false | |
} | |
}, | |
{ | |
freezeTableName: true, | |
tableName: 'org', | |
indexes: [ | |
{ | |
unique: false, | |
fields: ['name'] | |
} | |
] | |
} | |
); | |
return Org; | |
}; |
Sequelize Object : Role
이 개체는 매우 비슷해 보이며 여기에서 이름에 대한 추가 색인을 만들 것입니다.
export default (sequelize, DataTypes) => { | |
const Role = sequelize.define('role', | |
{ | |
id: { | |
type: DataTypes.UUID, | |
primaryKey: true, | |
defaultValue: DataTypes.UUIDV4 | |
}, | |
name: { | |
type: DataTypes.STRING(100), | |
unique: true, | |
allowNull: false | |
} | |
}, | |
{ | |
freezeTableName: true, | |
tableName: 'role', | |
indexes: [ | |
{ | |
unique: false, | |
fields: ['name'] | |
} | |
] | |
} | |
); | |
return Role; | |
}; |
Sequelize Object : User
User 개체는 orgId 필드를 통해 Org 테이블에 대한 참조와 Sequelize를 통해 생성되는 외래 키 제약 조건을 보유하고 있음을 알 수 있습니다. 이는 사용자와 조직 간의 일대 다 관계를 나타냅니다. 다시 이 고유 필드에 대한보다 효과적인 쿼리를 위해 직렬 필드에 추가 색인을 만들 것입니다.
export default (sequelize, DataTypes) => { | |
const User = sequelize.define('user', | |
{ | |
id: { | |
type: DataTypes.UUID, | |
primaryKey: true, | |
defaultValue: DataTypes.UUIDV4 | |
}, | |
serial: { | |
type: DataTypes.STRING(10), | |
unique: true, | |
allowNull: false | |
}, | |
firstName: { | |
type: DataTypes.STRING(100), | |
allowNull: false | |
}, | |
lastName: { | |
type: DataTypes.STRING(100), | |
allowNull: false | |
}, | |
orgId: { | |
type: DataTypes.UUID, | |
defaultValue: DataTypes.UUIDV4 | |
} | |
}, | |
{ | |
freezeTableName: true, | |
tableName: 'user', | |
indexes: [ | |
{ | |
unique: false, | |
fields: ['serial'] | |
} | |
] | |
} | |
); | |
return User; | |
}; |
Data Management
PostgreSQL에서 데이터베이스 테이블을 생성하는 방법을 간단히 살펴 보겠습니다.
잠깐만 요… 우리는 userrole 테이블에 대한 데이터 모델을 설명하지 않았습니다. 그렇죠? 다행스럽게도 Sequelize는 관계를 잘 관리하므로 그럴 필요가 없습니다.
// from src/db/model/index.js:
// define the relationships between the entitiesdb.user.belongsTo(db.org);
db.user.belongsToMany(db.role, { through: 'userrole' });
db.role.belongsToMany(db.user, { through: 'userrole' });
우리는 조직이 사용자와 일대 다 관계를 갖고 있으며 사용자와 역할이 테이블 userrole을 통해 다 대다 관계에 있음을 명시 적으로 sequelize에 알려줍니다. 그것은 모두 sequelize가 데이터의 정규화를 처리해야 한다는 것입니다. 즉, 사용자 역할 테이블에 상호 참조를 저장하는 것을 의미합니다.
Fetching Data
이 프로젝트는 GraphQL 플레이 그라운드를 제공하며 http : // localhost : 4000에서 액세스 할 수 있습니다. 이제 GraphQL의 기능을 활용하여 이전에 생성 한 테이블에서 매우 구체적인 데이터를 요청할 수 있습니다.
Query data from : Org
알려진 모든 조직을 표시하려면 다음 graphQL 쿼리를 사용하십시오.
query {
org {
count
data {
id
name
}
}
}
데이터 (조직 배열)와 개수에 대한 멋진 응답을 얻습니다.
이 예에서 PostgreSQL에 대한 쿼리는 다음과 같습니다.
SELECT “id”, “name” FROM “public”.”org” AS “org” WHERE 1=1;
Query data from : Role
역할에 대해서도 똑같이 할 수 있지만 여기서는 데이터의 수와 ID 검색을 건너 뜁니다.
query {
role {
data {
name
}
}
}
데이터 배열에 매우 구체적인 역할 이름 세트가 있습니다.
이 예에서 PostgreSQL에 대한 쿼리는 다음과 같습니다.
SELECT "name" FROM "public"."role" AS "role" WHERE 1=1;
꽤 깔끔하지만 아직 너무 화려하지는 않습니다. 조직 및 역할에 대한 1-n 및 n-m 관계가 모두 결합 된 사용자를 살펴 보겠습니다.
Query data from : User
다음 쿼리는 조직 이름 및 사용자가 가진 역할 목록을 포함하여 사용자의 세부 정보를 요청합니다.
query {
user {
count
data {
serial
firstName
lastName
org {
name
}
roles {
name
}
}
}
}
이제 조직과 역할이 사용자 레코드에 메시 된 중첩 된 개체로 표시되지만 사용자는 하나의 조직에만 연결될 수 있고 역할은 JSON 개체의 배열로 표시되므로 사용자는 JSON 개체로 표시됩니다. Tina처럼 여러 역할을 가질 수 있습니다.
이 예에서 PostgreSQL에 대한 쿼리는 다음과 같습니다.
SELECT | |
"user"."id", | |
"user"."serial", | |
"user"."firstName", | |
"user"."lastName", | |
"user"."orgId", | |
"user"."createdAt", | |
"user"."updatedAt", | |
"org"."id" AS "org.id", | |
"org"."name" AS "org.name", | |
"org"."createdAt" AS "org.createdAt", | |
"org"."updatedAt" AS "org.updatedAt", | |
"roles"."id" AS "roles.id", | |
"roles"."name" AS "roles.name", | |
"roles"."createdAt" AS "roles.createdAt", | |
"roles"."updatedAt" AS "roles.updatedAt", | |
"roles -> userrole"."userId" AS "roles.userrole.userId", | |
"roles -> userrole"."roleId" AS "roles.userrole.roleId", | |
"roles -> userrole"."createdAt" AS "roles.userrole.createdAt", | |
"roles -> userrole"."updatedAt" AS "roles.userrole.updatedAt" | |
FROM | |
"public"."user" AS "user" | |
LEFT OUTER JOIN | |
"public"."org" AS "org" | |
ON "user"."orgId" = "org"."id" | |
LEFT OUTER JOIN | |
( | |
"public"."userrole" AS "roles -> userrole" | |
INNER JOIN | |
"public"."role" AS "roles" | |
ON "roles"."id" = "roles -> userrole"."roleId" | |
) | |
ON "user"."id" = "roles -> userrole"."userId" | |
WHERE | |
1 = 1; |
요약
1n 및 n-m 관계가 있는 매우 간단한 관계형 데이터 모델을 Sequelize를 통해 PostgreSQL에서 JSON으로 관리하고 매핑 하는 방법을 살펴 보았습니다. 사용자 편의를 위해 JSON 데이터는 GraphQL을 통해 사용자에게 전달됩니다.
이 기사에서는 읽기 전용 작업에 중점을 두었습니다. 전체 CRUD 작업에 대한 또 다른 기사를 제공 할 계획입니다. 계속 지켜봐 주세요…
등록된 댓글이 없습니다.