Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Looking for the right tech stack for GraphQL ap...

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Looking for the right tech stack for GraphQL application

Nikita Galkin

March 16, 2019

Transcript

47 slides
  1. Nikita Galkin Love and Know: ▰ How to make developers

    and business happy ▰ Technical and process debt elimination Believe that: ▰ Any problem must be solved at the right level ▰ Software is easy. People are hard ▰ A problem should be highlighted, an idea should be "sold", a solution should be demonstrated Links: Site GitHub Twitter Facebook 2
  2. GraphQL is an open source query language created by Facebook

    The specification published in June 2018
  3. Honeymoon with GraphQL: • Reading docs • Exploring how to

    GraphQL • Comparing Apollo, Prisma, Relay, etc • Choosing IDE
  4. GraphQL Schema Definition Language: • A type has a name

    and can implement one or more interfaces • A field has a name and a type • The built-in scalar types are Int/Float/String/Boolean/ID • Enum is a scalar value that has a specified set of possible values
  5. I love to look in the mirrors Worse than D.R.Y.,

    DUPLICATION at: • Schema • DB tables • Endpoints Common solution: • Schema generation
  6. { __schema { types { __typename name } } }

    Introspection query Class reflections JavaScript have not Reflection
  7. import { Field, ID, ObjectType } from 'type-graphql'; import {

    Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; @Entity({ name: 'users' }) @ObjectType() export class User { @PrimaryGeneratedColumn('uuid') @Field(type => ID) id: string; @Column() @Index({ unique: true }) @Field() email: string; @Column() passwordHash: string; @Column({ nullable: true }) @Field({ nullable: true }) firstName?: string; @Column({ nullable: true }) @Field({ nullable: true }) lastName?: string; @Field() get name () { return `${this.firstName} ${this.lastName}`; } }
  8. import { Ctx, Query, Resolver } from 'type-graphql'; import {

    GraphQLContext } from '~/boundaries/graphql'; import { UsersRepository } from '~/repositories'; import { User } from '~/entities'; @Resolver(type => User) export class UserResolver { @Query(type => User) async currentUser ( @Ctx() context: GraphQLContext ): Promise<User> { return UsersRepository.find( { where: { id: context.user.id } }, ); } }
  9. I have Big Latency, perhaps Reasons: • Poor Performance •

    Too many DB queries • Client cache only Common solution: • Dataloader
  10. Precomputed data You store data for resolvers in NoSQL DB

    with structure similar GraphQL schema. Limitations: • You have data ownership
  11. We need data, but there are not decorators for such

    source type. For example: • Elastic Search • Data from external API • etc I miss something...
  12. // class class User { constructor(name, surname) { this.name =

    name; this.surname = surname; } } // class instance new User('Bart', 'Simpson'); // plain (literal) object ==============> const hero = { name: 'Gomer', surname: 'Simpson' }; POM
  13. import { plainToClass } from 'class-transformer'; fetch("users.json").then((users: Object[]) => {

    const realUsers = plainToClass(User, users); // now each user in realUsers is instance of User class }); const hero = { name: 'Gomer', surname: 'Simpson' }; hero instanceof User; // false
  14. Reasons: • SELECT all, because pagination and sorting implemented in

    the resolver • SELECT * FROM ... • JOIN is used I have Big Requests, Dear!
  15. GraphQL Resolver function signature: fieldName(obj, args, context, info) • obj:

    The object that contains the result returned from the resolver on the parent field • args: An object with the arguments passed into the field in the query • context: This is an object shared by all resolvers • info: AST, state of resolving. Example
  16. import { GraphQLResolveInfo } from 'graphql'; import graphqlFields from 'graphql-fields';

    import { Ctx, Info, Query, Resolver } from 'type-graphql'; import { GraphQLContext } from '~/boundaries/graphql'; import { UsersRepository } from '~/repositories'; import { User } from '~/entities'; @Resolver(type => User) export class UserResolver { @Query(type => User) async currentUser ( @Ctx() context: GraphQLContext, @Info() info: GraphQLResolveInfo ): Promise<User> { const fields = graphqlFields(info); return UsersRepository.find( { where: { id: context.user.id } }, { select: Object.keys(fields) } ); } }
  17. import graphqlFields from 'graphql-fields'; // ... async currentUser ( @Ctx()

    context: GraphQLContext, @Info() info: GraphQLResolveInfo ): Promise<User> { const fields = graphqlFields(info); return UsersRepository.find( { where: { id: context.user.id } }, { select: Object.keys(fields) } ); }
  18. • ACL • Query Complexity We have to talk about

    our relationship... import { Authorized } from 'type-graphql'; // ... @ObjectType() export class User { // ... @Authorized('Admin', 'CurrentUser') @Field() email: string; @Field(type => User,{ complexity: 20 }) friends: User[]; }
  19. Solutions: • For versioning graphql-doctor • For CI/CD eslint-plugin-graphql •

    For development WebStorm IDE plugin Let's not change anything?
  20. Ideas: • Have only one source of truth • Don’t

    repeat yourself • Use the right tools • Solve problem at the right level
  21. 47 THANKS! HAPPY CODING WITH GRAPHQL You can find me

    on Twitter as @galk_in Slides are available at speakerdeck.com/galkin or at my site galk.in

More Decks by Nikita Galkin

Other Decks in Programming