Last active
April 24, 2017 01:18
-
-
Save thebigredgeek/3e5d9757b054b642062569424f7fee03 to your computer and use it in GitHub Desktop.
RFC: "Solaris"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"redis": { | |
"host": "localhost", | |
"port": 6379, | |
"prefix": "solaris-" | |
}, | |
"port": 3000 | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { publish, channel, schema, gql, type, Service } from 'solaris'; | |
@type('Post') | |
@schema(gql` | |
type Post { | |
id: ID! | |
text: String | |
} | |
# Post lives elsewhere | |
extend type User { | |
# Magically binds to getPostsForAuthor function below (see decorator) | |
Posts: [Post]! | |
} | |
input CreatePostInput { | |
text: String | |
} | |
extend type Mutation { | |
# Automatically binds to the `createPost` function below | |
createPost (input: CreatePostInput): Post | |
} | |
extend type Subscription { | |
# Automatically binds to the `postsSubscription` function below | |
postsSubscription(input: PostSubscriptionInput!): Post | |
} | |
input PostSubscriptionInput { | |
AuthorId: ID | |
} | |
`) | |
export default class User extends Service { | |
@publish(post => { // Publish the returned value under the `userChannel` channel | |
channel: 'postsChannel', | |
object: post.toJSON() | |
}) | |
// Resolver for createPost | |
async createPost (root, args, context) { | |
return context.models.post.create(args.input, context.user); | |
} | |
// Resolver for postsSubscription | |
async postsSubscription (post, args, context) { | |
return post; | |
} | |
// Kinda like setupFunctions for graphql-subscriptions? | |
@channel({ | |
for: 'postsSubscription' | |
}) | |
async postsChannel (post, args, context) { | |
const { input: { AuthorId } } = args; | |
// Filter by AuthorId, if specified | |
return AuthorId ? post.AuthorId === AuthorId : true; | |
} | |
@edge({ | |
type: 'User', | |
field: 'Posts' | |
}) | |
getPostsForAuthor (user, args, context) { | |
return context.models.post.findByAuthorId(user.id); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { publish, channel, schema, gql, type, Service } from 'solaris'; | |
@type('User') | |
@schema(gql` | |
type User { | |
id: ID! | |
name: String | |
email: Email | |
} | |
# Post lives elsewhere | |
extend type Post { | |
# Magically binds to getAuthorForPost function below | |
Author: User! | |
} | |
input UpdateUserInput { | |
id: ID! | |
name: String | |
email: String | |
} | |
extend type Mutation { | |
# Automatically binds to the `updateUser` function below | |
updateUser (input: UpdateUserInput): User | |
} | |
extend type Subscription { | |
# Automatically binds to the `userSubscription` function below | |
userSubscription (input: UserSubscriptionInput!): User | |
} | |
input UserSubscriptionInput { | |
id: ID | |
} | |
`) | |
export default class User extends Service { | |
@publish(user => { // Publish the returned value under the `userChannel` channel | |
channel: 'userChannel', | |
object: user.toJSON() | |
}) | |
// Resolver for updateUser | |
async updateUser (root, args, context) { | |
return context.models.user.update(args.input); | |
} | |
// Resolver for userSubscription | |
async userSubscription (user, args, context) { | |
return user; | |
} | |
// Kinda like setupFunctions for graphql-subscriptions? | |
@channel({ | |
for: 'userSubscription' | |
}) | |
async userChannel (user, args, context) { | |
const { user: subscribedUser } = context; | |
return user.id === subscribedUser.id; | |
} | |
@edge({ | |
type: 'Post', | |
field: 'Author' | |
}) | |
getAuthorForPost (post, args, context) { | |
return context.models.user.findById(post.AuthorId); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Server is bootstrapped by running
solaris
from the CLI. Batteries included: body parsing, pluggable auth with later example, WS based subscriptions with redis pub/sub, logging, etc.Authentication could easily be implemented with additional decorators / HOF.
Boilerplate persistent storage + search via Mongo + ElasticSearch could also be easily implemented here, though optional, with separate package(s) like
solaris-mongo
andsolaris-elasticsearch
. The classes could double as models, with context factories and the like. Through this, you could do something like:From this, you'd get
this.mongo
or something like it. The same could be implemented with Elasticsearch. If we took this route, we'd need to provide lifecycle hooks as well... such ascreateContext
anddestroyContext
functions.As is,
solaris
would boot all service files into the same NodeJS process. However, organizing code this way with abstracted transport makes breaking into micro services trivial. Asolaris-cluster
lib could be easily written, which could serve as an API facade and dispatch portions of the query down to discrete services each on their own docker image. The entire cluster could be built viasolaris build
, which would pre-aggregate GQL types, distribute them between containers, etc... with the facade service handling routing to resolve entire queries with multiple services without having to think about how to handle transport etc.