Looking for the right tech stack for GraphQL application

Looking for the right tech stack for GraphQL application

A2443b68836f2d166eae52b940e99bf6?s=128

Nikita Galkin

March 16, 2019
Tweet

Transcript

  1. Looking for the right tech stack for GraphQL application Nikita

    Galkin
  2. 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
  3. GraphQL is an open source query language created by Facebook

    The specification published in June 2018
  4. What problems does GraphQL solve?

  5. Client Development is too expensive

  6. Ask for what you need, get exactly that

  7. Domain knowledge does not represent at a single place as

    source code
  8. By Dmytro Naumenko

  9. Describe what’s possible with a type system

  10. It is complicated to develop Server and Client in parallel

  11. None
  12. GraphQL is awesome!

  13. GraphQL is awesome!

  14. But… with GraphQL Server Development becomes much harder

  15. My story: Node.js and GraphQL during 1 year and 4

    projects
  16. Honeymoon with GraphQL: • Reading docs • Exploring how to

    GraphQL • Comparing Apollo, Prisma, Relay, etc • Choosing IDE
  17. GraphiQL

  18. GraphQL Playground

  19. Insomnia

  20. 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
  21. None
  22. I love to look in the mirrors Worse than D.R.Y.,

    DUPLICATION at: • Schema • DB tables • Endpoints Common solution: • Schema generation
  23. GraphQL at Facebook by Adam D.I. Kramer

  24. None
  25. { __schema { types { __typename name } } }

    Introspection query Class reflections JavaScript have not Reflection
  26. npm install: • reflect-metadata • typescript • type-graphql • any

    other useful decorators first packages
  27. 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}`; } }
  28. 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 } }, ); } }
  29. Documentation and examples: https://typegraphql.ml

  30. I have Big Latency, perhaps Reasons: • Poor Performance •

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

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

    source type. For example: • Elastic Search • Data from external API • etc I miss something...
  33. // 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
  34. 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
  35. https://github.com/typestack/class-transformer Serialize/Deserialize on steroids with decorators: • @Type • @Expose

    • @Exclude • Use discriminator for arrays!
  36. Reasons: • SELECT all, because pagination and sorting implemented in

    the resolver • SELECT * FROM ... • JOIN is used I have Big Requests, Dear!
  37. Precomputed Materialized View

  38. Sorting, filtering and pagination should be implemented on Data Layer

  39. 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
  40. 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) } ); } }
  41. 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) } ); }
  42. • 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[]; }
  43. Solutions: • For versioning graphql-doctor • For CI/CD eslint-plugin-graphql •

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

    repeat yourself • Use the right tools • Solve problem at the right level
  47. 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