Skip to content

A implementation example of using Graphql and Sequilize ORM

Notifications You must be signed in to change notification settings

eduardosilva/graphql-sequelize

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Graphql and Sequelize

This is a example about how to start a project using Graphql, NodeJs and Sequelize ORM. All examples were made using AdventureWorks database.

Table of Content

  1. Setup
  2. Mappings and Configurations
  3. Queries
  4. Useful links

Setup

IMPORTANT: In this project example I'm not using sequelize-cli to create my models.

Mappings and Configurations

Focus on Graphql schema not in your database design

Build your GraphQL schema to express "what" rather than "how". Then you can improve your implementation details without breaking the interface with older clients. https://graphql.github.io/learn/thinking-in-graphs/#working-with-legacy-data

Your schema model don't need to be just like your database table model, actually I recommend you to design your schema without thinking about the implementation when it's possible.

When designing APIs, ask yourself, “What if the implementation changes?” A change such as moving the database from SQL to Mongo. Does the API continue to make sense after those changes? This best pratice allows us to prototype quickly, scale easily, and deploy new services without interrupting the client. https://medium.com/@zavilla90/best-practices-in-designing-graphql-apis-395225bdcd1

For instance:

The Employee has a name as a property but in the Database the name is in the Person table, so I need to improve a solution in my resolver

// Employee schema
export const typeDefs = gql`
    ...
    type Employee { 
        id: ID!
        name: String 
        ... 
    }
`;

// Employee Sequelize Mapping
export const EmployeeFactory = (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes): Sequelize.Model<EmployeeInstance, EmployeeAttributes> => {
    const attributes: SequelizeAttributes<EmployeeAttributes> = {
        id: {
            type: Sequelize.INTEGER,
            autoIncrement: false,
            primaryKey: true,
            field: 'BusinessEntityID'
        },
        ...
    }

    const Employee = sequelize.define<EmployeeInstance, EmployeeAttributes>('Employee', attributes, { schema: 'HumanResources', freezeTableName: true, timestamps: false, });

    Employee.associate = models => {
        Employee.hasOne(models.Person, { as: 'person', foreignKey: 'BusinessEntityId' })
        ...
    }

    return Employee;
};

// Employee name resolver
export const resolvers = {
    ... 
    Employee: {
        name: async ({ id }: { id: number }) => {
            const person = await personLoader.load(id) as any
            return `${person.firstName} ${person.middleName} ${person.lastName}`
        }
    }
}

Queries

Use DataLoader

Facebook DataLoader is a generic utility used to abstract request batching and caching. This is a perfect solution to avoid Select N+1 problem. For instance:

Example without DataLoader

export const resolvers = {
    ... 
    Employee: {
        name: async ({ id }: { id: number }) => {
            //  without dataloader
            const person = await db.Person.find({
                where: { id  }
            }) as any

            return `${person.firstName} ${person.middleName} ${person.lastName}`
        }
    }
}
Result: N+1 problem !!!
...
[1] Executing (default): SELECT [BusinessEntityID] AS [id], [personType], [nameStyle], [title], [firstName], [middleName], [lastName], [BusinessEntityId] FROM [Person].[Person] AS [Person] WHERE [Person].[BusinessEntityID] = 284;
[1] Executing (default): SELECT [BusinessEntityID] AS [id], [personType], [nameStyle], [title], [firstName], [middleName], [lastName], [BusinessEntityId] FROM [Person].[Person] AS [Person] WHERE [Person].[BusinessEntityID] = 285;
[1] Executing (default): SELECT [BusinessEntityID] AS [id], [personType], [nameStyle], [title], [firstName], [middleName], [lastName], [BusinessEntityId] FROM [Person].[Person] AS [Person] WHERE [Person].[BusinessEntityID] = 286;
...

Example with DataLoader

const personLoader = new DataLoader<number, any[]>(async (ids: number[]): Promise<any[]> => {
    const result = await db.Person.findAll({
        where: {
            id: {
                [Op.in]: ids
            }
        }
    })
    return result
});

export const resolvers = {
    ... 
    Employee: {
        name: async ({ id }: { id: number }) => {
            const person = await personLoader.load(id) as any
            return `${person.firstName} ${person.middleName} ${person.lastName}`
        }
    }
}
Result: One query for all employees

[1] Executing (default): SELECT [BusinessEntityID] AS [id], [personType], [nameStyle], [title], [firstName], [middleName], [lastName], [BusinessEntityId] FROM [Person].[Person] AS [Person] WHERE [Person].[BusinessEntityID] IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290);

Useful links:

Releases

No releases published

Packages

No packages published