GraphQL Protocol Plugin for ObjectStack
This plugin provides GraphQL protocol support for ObjectStack applications using Apollo Server. It automatically generates GraphQL schemas from ObjectStack metadata and provides query and mutation resolvers.
Based on reference implementation by @hotlong
- ✅ Automatic Schema Generation - Generate GraphQL types from ObjectStack metadata
- ✅ Query Resolvers - Query individual records and lists
- ✅ Mutation Resolvers - Create, update, and delete operations
- ✅ Apollo Server Integration - Full Apollo Server 4.x support
- ✅ Introspection - GraphQL schema introspection
- ✅ GraphQL Playground - Interactive GraphQL IDE
- ✅ No Direct DB Access - All operations through ObjectStackRuntimeProtocol
pnpm add @objectql/protocol-graphql @apollo/server graphqlimport { ObjectKernel } from '@objectstack/runtime';
import { GraphQLPlugin } from '@objectql/protocol-graphql';
const kernel = new ObjectKernel([
new GraphQLPlugin({
port: 4000,
introspection: true,
playground: true
})
]);
await kernel.start();
// GraphQL endpoint: http://localhost:4000/interface GraphQLPluginConfig {
/** Port to listen on (default: 4000) */
port?: number;
/** Enable introspection (default: true) */
introspection?: boolean;
/** Enable GraphQL Playground (default: true) */
playground?: boolean;
/** Custom type definitions (optional) */
typeDefs?: string;
}The plugin automatically generates a GraphQL schema from your ObjectStack metadata.
kernel.metadata.register('object', 'users', {
name: 'users',
fields: {
name: { type: 'text', required: true },
email: { type: 'email', required: true },
active: { type: 'boolean', default: true }
}
});type Query {
# Get single user by ID
users(id: String!): Users
# Get list of users
usersList(limit: Int, offset: Int): [Users!]!
# Get object metadata
getObjectMetadata(name: String!): String
# List all objects
listObjects: [String!]!
}
type Mutation {
# Create user
createUsers(input: String!): Users
# Update user
updateUsers(id: String!, input: String!): Users
# Delete user
deleteUsers(id: String!): Boolean
}
type Users {
id: String!
name: String!
email: String!
active: Boolean
}query {
users(id: "123") {
id
name
email
active
}
}query {
usersList(limit: 10, offset: 0) {
id
name
email
}
}mutation {
createUsers(input: "{\"name\":\"Alice\",\"email\":\"alice@example.com\"}") {
id
name
email
}
}mutation {
updateUsers(
id: "123"
input: "{\"name\":\"Alice Updated\"}"
) {
id
name
email
}
}mutation {
deleteUsers(id: "123")
}query {
# Get metadata for users object
getObjectMetadata(name: "users")
# List all registered objects
listObjects
}ObjectQL field types are automatically mapped to GraphQL types:
| ObjectQL Type | GraphQL Type |
|---|---|
| text, email, url, etc. | String |
| number, currency, percent | Float |
| autonumber | Int |
| boolean | Boolean |
| date, datetime, time | String (ISO 8601) |
| select | String |
| lookup, master_detail | String (ID reference) |
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000/',
cache: new InMemoryCache()
});
const { data } = await client.query({
query: gql`
query {
usersList {
id
name
email
}
}
`
});curl -X POST http://localhost:4000/ \
-H "Content-Type: application/json" \
-d '{
"query": "{ usersList { id name email } }"
}'This plugin follows the ObjectStack protocol plugin pattern:
export class GraphQLPlugin implements RuntimePlugin {
private protocol?: ObjectStackRuntimeProtocol;
async install(ctx: RuntimeContext) {
// Initialize protocol bridge
this.protocol = new ObjectStackRuntimeProtocol(ctx.engine);
}
async onStart(ctx: RuntimeContext) {
// Generate schema from metadata
const typeDefs = this.generateSchema();
const resolvers = this.generateResolvers();
// Start Apollo Server
this.server = new ApolloServer({ typeDefs, resolvers });
}
}All data operations use the protocol bridge:
this.protocol.getMetaTypes()- Get object typesthis.protocol.getData(objectName, id)- Get single recordthis.protocol.findData(objectName, query)- Query recordsthis.protocol.createData(objectName, data)- Create recordthis.protocol.updateData(objectName, id, data)- Update recordthis.protocol.deleteData(objectName, id)- Delete record
Run GraphQL alongside other protocols:
import { ObjectKernel } from '@objectstack/runtime';
import { GraphQLPlugin } from '@objectql/protocol-graphql';
import { ODataV4Plugin } from '@objectql/protocol-odata-v4';
import { JSONRPCPlugin } from '@objectql/protocol-json-rpc';
const kernel = new ObjectKernel([
new GraphQLPlugin({ port: 4000 }),
new ODataV4Plugin({ port: 8080 }),
new JSONRPCPlugin({ port: 9000 })
]);
await kernel.start();
// Access same data through different protocols:
// - GraphQL: http://localhost:4000/
// - OData V4: http://localhost:8080/odata
// - JSON-RPC: http://localhost:9000/rpcYou can extend the auto-generated schema with custom types:
new GraphQLPlugin({
port: 4000,
typeDefs: `
type CustomType {
field1: String
field2: Int
}
extend type Query {
customQuery: CustomType
}
`
})The GraphQL plugin uses Apollo Server 4.x, which supports plugins for logging, caching, etc.
- Input types use JSON strings for simplicity (can be enhanced with proper GraphQL input types)
- Relationships are represented as ID strings (can be enhanced with GraphQL field resolvers)
- Filtering and sorting not yet supported in list queries (coming soon)
MIT
Reference Implementation: Based on code example provided by @hotlong