Graphex Core
Description
This package allows you to automatically generate Apollo Server schema and resolvers for MongoDB using Prisma-like SDL.
Quick preview on codesandbox
Note! In Codesandbox any change in the code will restart the service. In this setup we use in memory DB, hence any restart will purge the data you'll create with Mutations
Installation
With yarn:
yarn add @graphex/core
or using npm:
npm install --save @graphex/core
Usage
Project initialization is the same as for Apollo Server. The only difference is that we use makeExecutableSchema
from this package to generate schema.
import ApolloModelMongo, { QueryExecutor } from '@graphex/core';
const schema = await new ApolloModelMongo({
queryExecutor: QueryExecutor(db),
}).makeExecutablSchema({
typeDefs,
});
const server = new ApolloServer({
schema,
});
You can find full examples here
SDL example
type Category @model {
id: ObjectID! @id @unique @db(name: "_id")
title: String @default(value: "New Category")
parentCategory: Category @relation(storeField: "parentCategoryId")
subcategories: [Category!] @extRelation(storeField: "parentCategoryId")
posts: [Post!] @extRelation
createdAt: Date @createdAt
updatedAt: Date @updatedAt
}
type Comment {
body: String
user: User! @relation
}
type Post @model {
id: ObjectID! @id @unique @db(name: "_id")
title: String!
body: String!
category: Category @relation
keywords: [String!]
owner: User! @relation
place: GeoJSONPoint
comments: [Comment!]
}
interface User @inherit @model {
id: ObjectID! @id @unique @db(name: "_id")
username: String! @unique
}
enum AdminRole {
superadmin
moderator
}
type Admin implements User {
role: AdminRole
}
enum SubscriberRole {
free
standard
premium
}
type SubscriberProfile {
firstName: String!
lastName: String!
}
type Subscriber implements User {
role: SubscriberRole
profile: SubscriberProfile!
}
The above SDL generates this endpoint https://apollo-model-mongodb-example.now.sh
Example queries below
Directives
model
directive
The - Connects object with MongoDB collection.
- Valid locations: OBJECT or INTERFACE
- Optional
- Arguments
- collection:String
- The name of MongoDB collection * Optional (Default value is pluralized name of the object)
unique
directive
The - Add field to WHERE_UNIQUE input type
- Valid locations: FIELD
- Optional
id
directive
The - Mark field as identifier. Skip creation.
- Valid locations: FIELD
- Optional
db
directive
The - Map GraphQL field to collection.
- Valid locations: FIELD
- Optional
- Arguments
- name:String
- The name of field in collection * Required
default
directive
The - Sets a default value for a field.
- Valid locations: FIELD
- Optional
- Arguments
- value:String
- The default value for the field * Required
inherit
directive
The - Clones interface fields to objects.
- Valid locations: INTERFACE
- Required
discriminator
directive
The - Used to define field and values to resolve implementation type.
- Valid locations: INTERFACE or OBJECT
- Optional
- Arguments
- value:String
- Required
relation
directive
The - Used to define relation between two collections.
- Valid locations: FIELD
- Optional
- Arguments
- field:String
- Optional
- Default value: _id
- storeField:String
- Optional
- Default value:
${TypeName}Id${s}
extRelation
directive
The -
Used to define external relation between two collections (identifiers stored in related documents).
-
Valid locations: FIELD
-
Optional
-
Arguments
-
field:String
- Optional
- Default value: _id
-
storeField:String
- Optional
- Default value:
${TypeName}Id${s}
-
many:Boolean
- Optional
- Default value: false
-
createdAt
directive
The - Sets date on field on CREATE.
- Valid locations: FIELD
- Optional
updatedAt
directive
The - Sets date on field on CREATE and UPDATE.
- Valid locations: FIELD
Serverless
You can use this package with serverless environments. Read more here. Also take a look at example-now if you are using Zeit Now.
Customization
- You can define your own scalars and directives as for usual Apollo server.
- You can add custom modules at MongoModel stage (docs coming soon)
- All queries to DB executes with QueryExecutor function. This package has predefined one, but you can override it and add hooks or check user authorization.
const QueryExecutor = ({ type, collection, doc, docs, selector, options })=>Promise
Contribution
You are welcome to open Issues, Feature Requests and PR with new features and bug fixes
Roadmap
- Filter by Nth array element
- Add subscriptions
- Release stable version 1.0.0
- Add Moment scalar
- Improve Geo queries support
Features
- Simple query
- Simple create
- Filter
- Difficult filter
- Relation query
- Relation filter
- Relation create
- Nested create
- Interfaces
- Geo queries
Simple query
query
{
categories {
id
title
}
}
response
{
"data": {
"categories": [
{
"id": "5c3f4d84e98bd4e76e1d34d1",
"title": "root"
},
{
"id": "5c3f4dd3e98bd4e76e1d34d2",
"title": "JS"
},
{
"id": "5c3f4f46e98bd4e76e1d34d3",
"title": "MongoDB"
}
]
}
}
Simple create
query
mutation {
createCategory(data: { title: "root" }) {
id
}
}
response
{
"data": {
"createCategory": {
"id": "5c3f4d84e98bd4e76e1d34d1"
}
}
}
Filter
request
{
categories(where: { title: "root" }) {
id
title
}
}
response
{
"data": {
"categories": [
{
"id": "5c3f4d84e98bd4e76e1d34d1",
"title": "root"
}
]
}
}
Difficult filter
request
{
categories(where: { OR: [{ title: "root" }, { title: "JS" }] }) {
id
title
}
}
response
{
"data": {
"categories": [
{
"id": "5c3f4d84e98bd4e76e1d34d1",
"title": "root"
},
{
"id": "5c3f4dd3e98bd4e76e1d34d2",
"title": "JS"
}
]
}
}
Relation query
query
{
categories {
id
title
parentCategory {
title
}
}
}
response
{
"data": {
"categories": [
{
"id": "5c3f4d84e98bd4e76e1d34d1",
"title": "root",
"parentCategory": null
},
{
"id": "5c3f4dd3e98bd4e76e1d34d2",
"title": "JS",
"parentCategory": {
"title": "root"
}
},
{
"id": "5c3f4f46e98bd4e76e1d34d3",
"title": "MongoDB",
"parentCategory": {
"title": "root"
}
}
]
}
}
Relation filter
query
{
categories(where: { parentCategory: { title: "root" } }) {
id
title
}
}
response
{
"data": {
"categories": [
{
"id": "5c3f4dd3e98bd4e76e1d34d2",
"title": "JS"
},
{
"id": "5c3f4f46e98bd4e76e1d34d3",
"title": "MongoDB"
}
]
}
}
Relation create
query
mutation {
createCategory(
data: {
title: "Mongodb"
parentCategory: { connect: { id: "5c3f4d84e98bd4e76e1d34d1" } }
}
) {
id
}
}
response
{
"data": {
"createCategory": {
"id": "5c3f4f46e98bd4e76e1d34d3"
}
}
}
Nested create
query
mutation {
createSubscriber(
data: {
username: "subscriber1"
profile: { create: { firstName: "Gwion", lastName: "Britt" } }
}
) {
id
username
}
}
response
{
"data": {
"createSubscriber": {
"id": "5c3f555b190d25e7bda1dea2",
"username": "subscriber1"
}
}
}
Interfaces
Connect
query
mutation {
createPost(
data: {
title: "Build GraphQL API with Apollo"
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
owner: { connect: { Admin: { username: "admin" } } }
}
) {
id
}
}
response
{
"data": {
"createPost": {
"id": "5c401347de7e67e9540abad2"
}
}
}
Query
query
{
posts {
title
owner {
username
... on Subscriber {
profile {
firstName
lastName
}
}
}
}
}
response
{
"data": {
"posts": [
{
"title": "Build GraphQL API with Apollo",
"owner": {
"username": "admin"
}
}
]
}
}
Geo queries
query
{
posts(
where: {
place_near: {
geometry: { type: Point, coordinates: [0, 51.01] }
maxDistance: 10000
}
}
) {
id
title
place {
distance(toPoint: { type: Point, coordinates: [0, 51.01] })
}
}
}
response
{
"data": {
"posts": [
{
"id": "5c401347de7e67e9540abad2",
"title": "Build GraphQL API with Apollo",
"place": {
"distance": 1111.9492664453662
}
}
]
}
}