How I helped improve Angular Console
By Doing GraphQL Right
Did you know that Angular Console uses GraphQL under the hood? I want to tell about how it used it and how I helped to improve it because that might be useful for people trying to implement GraphQL in their applications, both on client and server.
Angular Console is a user interface for Angular CLI created by Nrwl, widely used in Angular Community.
I will link to the PRs I’ve made to Angular Console throughout the article, so you could see everything I recommend in practice.
After reading the announcement of Angular Console I got very excited about the tool and immediately decided to explore the codebase. I noticed Electron and that the project is based on Angular CLI and Nrwl’s NX.
That’s super cool but what I found the most interesting was GraphQL.
As a freelancer, I work on daily basis with The Guild. Most of our projects are built with GraphQL. Throughout the 3 years of adopting it, our team tested practices and developed open source tools that helped to improve our workflow.
So when I saw the first implementation, I thought it would be nice to share some ideas and implement some code that might help to improve the GraphQL part of Angular Console.
Apollo Angular as the GraphQL Client
I was hoping to find Apollo Angular as one of dependencies. I might be a bit bias as the author of that library but our team used it in all of our angular based projects with huge success.
KLM and AirFrance runs on Apollo Angular
Okay, but just like in REST, you don’t need sophisticated tools to communicate with the API. Simple
fetch
or Angular’s HttpClient
is far enough. Why then the GraphQL client?
Having a client, like Apollo, allows you to easily execute GraphQL operations and by having a cache layer, fetched data stays consistent across all components. Dhaivat Pandya explains it well in his “Why you might want a GraphQL client” post.
Apollo has a comprehensive documentation that covers a lot of use cases, and I highly recommend to read it.
Using DI to Create Apollo
Angular Console used an old way of initializing Apollo. In one of the recent versions of Apollo
Angular I introduced APOLLO_OPTIONS
, an InjectionToken that provides a configuration object to
Apollo service. The old API caused an issue with a race condition where a service tried to use
Apollo before it got created.
That was the first, very small PR. Next PR brought more changes and was focused only on the server.
Apollo Server 2.0
I replaced express-graphql
with a more complete solution, Apollo Server. This move helped to
improve developer experience by having a built-in support for GraphQL Subscription, file
uploading and error handling. I’m pretty sure the team behind Angular Console have plans to take
advantage of it and implement subscriptions in the app, for example to replace currently used
polling technique.
Schema Definition Language
SDL, in short, is a syntax that allows to define GraphQL Schema, so instead of using GraphQL’s API, you simply write everything as string.
For example, using GraphQLObjectType
might look like this:
new GraphQLObjectType({
name: 'Post',
fields: {
id: {
type: GraphQLString
},
text: {
type: GraphQLString
}
}
})
with Schema Definition Language:
type Post {
id: String
text: String
}
In my opinion, it’s more convenient and way more intuitive to work with.
Keeping Resolve Functions Separated from SDL
In our projects, we try to group resolvers by GraphQL type and have them nearby the corresponding schema definition.
Having both, type definition and resolve functions in the GraphQLObjectType
looks like that:
new GraphQLObjectType({
name: 'Post',
fields: {
id: {
type: GraphQLString,
resolve: parent => parent._id
},
text: {
type: GraphQLString,
resolve: parent => parent.content
}
}
})
I personally think it was a good choice because it forces developers to write logical part right next to type definition. The problem is, the bigger types the more confusing it gets. Also keeping resolvers as standalone functions makes them easier to test.
With Schema Definition Language, it’s looks way better.
const PostType = gql`
type Post {
id: String
text: String
}
`
const Post = {
id: parent => parent._id,
text: parent => parent.content
}
Here are the relevant changes that I’ve mentioned above, that allowed me to introduce something really interesting in the next PR.
https://github.com/nrwl/nx-console/pull/175Apollo Server 2.0 Latest Apollo Angular refactoring — moved files under /api directory used SDL instead of classes from…github.com’)
Strongly Typed Resolvers
We love TypeScript, and we saw an opportunity to take our GraphQL
servers to the next level. Instead of having any
or defining interfaces for each resolver by hand,
we decided to take advantage of one of our tools, called
GraphQL Code Generator (thanks Dotan Simha for creating it).
In short, it’s a tool to generate pretty much any piece of code, based on a GraphQL Schema. We use it a lot, mostly for types (server and client) but also to create MongoDB models, introspection files, Angular components and more.
In Angular Console, I used the TypeScript plugins to generate types for a schema and also for GraphQL Resolvers. It’s one of the pieces that makes your code even more strongly typed, from end to end.
Here’s how it might look like.
import { PostResolvers } from './generated-types'
const Post: PostResolvers.Resolvers = {
id: parent => parent._id,
text: parent => parent.content
}
export interface PostParent {
_id: string
content: string
}
If you want to take a look at the changes and read about GraphQL Code Generator:
https://github.com/nrwl/nx-console/pull/185We recently released another new version of the GraphQL Code Generator that fixed a lot of issues, introduced a feature called Mappers, made signatures of resolve functions more strict and handles multiple results in parallel.
https://github.com/nrwl/nx-console/pull/413The GraphQL Code Generator is one powerful beast that enables any kind of code generation based just on GraphQL Schema (you can create your own custom generation templates).
Named Operations
GraphQL in most cases allows to use a shorthand syntax but putting a type and a name of an operation is very useful, simply for debugging and logging. It’s easier to track down a failed operation, because it’s no longer anonymous and by keeping all names unique you’re able to take advantage of any tool or service. One tool I described in the next chapter.
Strongly Typed Operations and Code Generation
Fetching data with Apollo Angular, requires few steps:
- Import
Apollo
service - Inject the service in a component
- Define GraphQL operation
- Wrap the operation with the
gql
tag - Call
Apollo.watchQuery
with the operation - Get an
Observable
with data
That’s a lot, and in order to have everything strongly typed you even have to define extra interfaces that are specific for each operation.
import { Apollo } from 'apollo-angular'
import gql from 'graphql-tag'
interface Post {
id: string
text: string
}
interface PostQuery {
post: Post
}
@Component({
/*...*/
})
export class PostComponent {
@Input() postId: string
post: Observable<Post>
constructor(private apollo: Apollo) {}
ngOnInit() {
this.post = this.apollo
.watchQuery<PostQuery>({
query: gql`
query getPost($id: String!) {
post(id: $id) {
id
text
}
}
`,
variables: {
id: this.postId
}
})
.valueChanges.pipe(map(result => result.data.post))
}
}
I wanted to share with Angular Console, something that we use and what helped to improve our workflow.
One interesting thing that we’re able to achieve is the
apollo-angular
code-generator plugin.
Its main purpose is to generate strongly typed services for each GraphQL operation. Take a look at the following scientific visualization:
Given the example I previously used, this is how it might look like with Apollo Angular plugin now.
- Write a query in a
.graphql
file - Run the codegen (has watch mode)
- Use a fully typed generated Angular service directly in your component
query getPost($id: String!) {
post(id: $id) {
id
text
}
}
import { GetPostGQL, Post } from './generated/graphql';
@Component({...})
export class PostComponent {
@Input() postId: string;
post: Observable<Post>;
constructor(
private getPostGQL: GetPostGQL
) {}
ngOnInit() {
this.post = this.getPostGQL
.watch({ id: this.postId })
.valueChanges
.pipe(
map(result => result.data.post)
);
}
}
As you can see, we no longer use Apollo service directly (it’s used under the hood) and every operation has now strongly typed API.
It wouldn’t be possible without introducing this new API. I highly recommend to read an article linked below, it explains what it is and how it could be used with the codegen.
/blog/apollo-angular-12I also prepared an explanation video that might help you to learn step by step, what code generation is and how to use it in a project.
Here is the relevant PR introducing this change into Angular Console:
https://github.com/nrwl/nx-console/pull/219https://github.com/nrwl/nx-console/pull/263
Summary
GraphQL is a very useful and fast growing technology. It helps with so many different use cases of developing applications, large and small. But don’t forget that the ecosystem of GraphQL is huge and there are a lot of extra tools and best practices that might make it even more useful!
I hope this post was helpful for you to learn about some handy things in GraphQL.
Join our newsletter
Want to hear from us when there's something new?
Sign up and stay up to date!
*By subscribing, you agree with Beehiiv’s Terms of Service and Privacy Policy.