-
Notifications
You must be signed in to change notification settings - Fork 275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to do schema stitching with Nexus? #70
Comments
I think we can just use graphql-tools for this. Since the output of nexus makeSchema function is a graphql js schema, one can easily use packages from the existing ecosystem like graphql-shield and many others. import {
makeExecutableSchema,
addMockFunctionsToSchema,
mergeSchemas,
} from 'graphql-tools';
import { makeSchema } from 'nexus';
const nexusSchema = makeSchema({
types: [Account, Node, Query, StatusEnum],
// or types: { Account, Node, Query }
// or types: [Account, [Node], { Query }]
});
const authorSchema = makeExecutableSchema({
typeDefs: `
type User {
id: ID!
email: String
}
type Query {
userById(id: ID!): User
}
`
});
addMockFunctionsToSchema({ schema: authorSchema });
export const schema = mergeSchemas({
schemas: [
authorSchema,
nexusSchema
],
}); |
@pantharshit00 oh that is great thank you! Just to clarify a couple of things:
export const schema = mergeSchemas({
schemas: [
chirpSchema,
nexusSchema
],
}); Did you mean 😄 Also do you know if it would be possible to also extend the merged schema with nexus, rather than with graphql tool mergeSchema? return mergeSchemas({
schemas: [
bookingSchema,
customerSchema
],
resolvers: {
Booking: {
customer: {
fragment: `... on Booking { id }`,
resolve(booking, args, context, info) {
return info.mergeInfo.delegateToSchema({
schema: customerSchema,
operation: "query",
fieldName: "customers",
args: {
where: { id: booking.customerId }
},
context,
info
});
}
}, To be fair, probably it is no point to try to do it differently. |
@otrebu there are some good solutions for doing schema extension and composition with other GraphQL schemas in Nexus that I need to document / add a few new options for. Unfortunately don't have time to fully go into them right this minute, but I'll leave this ticket open and report back once I have some time to document/add some examples for how to do this. Would you be able to share the general structure of how your schema is split up and how you'd expect a tool that allowed schema stitching with nexus to work? Would most types be written in nexus and only some would be stitched in? Also, have you seen the extendType api for building a schema from multiple places in the codebase? There's also a mutationField and I'll soon add a similar |
doing schema stitching using using extendType is problematic if the schema is create from a remote schema, because the typechecking have no idea the type to extends exists |
@tgriesser thank you for reply, and thank you @kandros for your comment. I would need to create another example, as what I am working on it is for a client. It might take me a while before I can find the time to do this, but I can try. I need to look into extendType, as I didn't see it yet. |
@otrebu most of the example are using features not yet documented, so it's a great place to look into |
Hi @tgriesser do you have any update about the documentation? I started to implement stitching alongside nexus like @pantharshit00 kindly showed me. But then I found myself in the scenario (or similar at least) @kandros mentioned! I would like to add query and mutations that return types that are defined in remote schemas (in my scenario I have full control on those). Otherwise I should merge these schemas, use the converter. It is not optimal I would say. |
It's possible, from an introspected schema we could generate nexus code but pretty hard to accomplish, it needs lot of tooling work but looks doable. I suggest you take a look at nexus codebase to get a grasp of how typings generation is applied, what happens is crazy but easy to follow along 😁 |
@kandros I horribly done that before, I was hoping to be a temporary solution though. I have done something like this(horrible): import * as fs from "fs";
import { parse, DocumentNode, buildASTSchema, printSchema } from "graphql";
import { convertSDL } from "nexus";
const sdl = fs.readFileSync(__dirname + "/unified-graphql/schema.graphql", "utf-8");
const parsedSchema = parse(sdl);
const newDefinitions = parsedSchema.definitions
.filter(
(d: any) =>
!d.name.value.includes("Query") &&
!d.name.value.includes("Mutation") &&
!d.name.value.includes("Subscription") &&
!d.name.value.includes("Edge") &&
!d.name.value.includes("Aggregate") &&
!d.name.value.includes("Connection")
);
const filteredSchema: DocumentNode = {
kind: "Document",
definitions: newDefinitions,
loc: parsedSchema.loc
};
const newSchema = buildASTSchema(filteredSchema);
const codeFirstString = convertSDL(printSchema(newSchema));
fs.writeFileSync(__dirname + "/someTypesFromPrisma.ts", codeFirstString, {
encoding: "utf-8"
}); |
Hi @tgriesser, sorry to tag you again. It would be good to have your view. I see this topic being related to this one on the It would be good to be part of |
@tgriesser to answer your question 😄
At the moment I am doing something like this: const stitchSchemas = async () => {
const organisationSchema = await getRemoteSchema(
prismaEndpoints.organisations
);
const customerSchema = await getRemoteSchema(prismaEndpoints.customers);
return mergeSchemas({
schemas: [
organisationSchema,
customerSchema,
nexusSchema,
linkTypeDefs // where I extend some types from organisationSchema and customerSchema
],
resolvers: {
/// resolvers to delegate with the extended schema
}
})};
So if I create a mutation using const Mutation = mutationType({
definition(t) {
t.field("createCustomerFromPlainPassword", {
type: "Customer", // I can't do this 😢
args: {
input: arg({ type: createCustomerFromPlainPasswordInput })
},
resolve: async (
root,
{ input },
{ customerService },
info
) => {
const newCustomer = await customerService.createCustomer(
input
);
return newCustomer;
}
});
}
}); I can't do that, So when you ask me what I would really like to be able to do, it is to stitch from const nexusSchema = makeSchema({
externalSchemasToStitchIn:{
schemas: [customerSchema, organisationSchema],
excludeTypes: ["Role"]
}
types: [CreateCustomerFromPlainPasswordInput, Query, Mutation],
shouldGenerateArtifacts: true,
outputs: {
schema: path.join(__dirname, "./generated/schema.graphql"),
typegen: path.join(__dirname, "./generated/types.ts")
},
typegenAutoConfig: {
contextType: "ctx.IContext",
sources: [
{
alias: "ctx",
source: path.join(__dirname, "../context.ts")
}
]
}
}); So that when I extend my Mutation type I can return my |
Thanks for the info - I need to think a little more about how this will work and come up with some recommended patterns here. I'm considering incorporating something similar to the tools in Prisma to handle schema delegation - but it require a decent bit of work, so no ETA on it at the moment. By the way, have you tried doing it the other way around, feeding the types generated from the merged schemas into Nexus? Haven't tried it, but something like: const mergedSchemas = mergeSchemas({
schemas: [
organisationSchema,
customerSchema,
],
resolvers: {
/// resolvers to delegate with the extended schema
}
})};
const addedMutationField = mutationField("createCustomerFromPlainPassword", {
type: "Customer", // should be able to do this now.
args: {
input: arg({ type: createCustomerFromPlainPasswordInput })
},
resolve: async (
root,
{ input },
{ customerService },
info
) => {
const newCustomer = await customerService.createCustomer(
input
);
return newCustomer;
}
});
const finalSchema = makeSchema({
types: [schema.getTypeMap(), addedMutationField],
shouldGenerateArtifacts: true,
outputs: {
schema: path.join(__dirname, "./generated/schema.graphql"),
typegen: path.join(__dirname, "./generated/types.ts")
},
typegenAutoConfig: {
contextType: "ctx.IContext",
sources: [
{
alias: "ctx",
source: path.join(__dirname, "../context.ts")
}
]
}
}); |
Thought about this more after making the previous comment and this approach won't quite work as I mentioned - though it's how it should work eventually once the appropriate changes are made to support this. Will work on some approaches for this and keep you posted once there's something to try out here. |
@tgriesser thanks! I see this topic being related to this from the What are your thoughts on that? Just to give you an idea, this is the infrastructure I am working on: Then we have another 3 graphql servers in front of the unified graph server. On these we will do the auth, maybe extending/hiding some queries/mutations/types. If you could help me to help you and help @Weakky . Cheers! |
Any update on this given the recent transition to Nexus Framework? I would love to be able to combine several nexus microservices using a nexus gateway in some way that allows me to reuse my types from the microservices in the gateway code. |
Same here. I got an apollo server with the old nexus schema (and sequelize) and building a new service with nexus framework (and prisma) and want to gradually move over the entire system, but its tightly coupled so as soon as I move one objectType over, it has fields that references other ObjectTypes and I keep moving down a rabbithole, so need schema stitching or federtion or something to allow all fields that aren't yet implemented to default back to the old service |
Same here as well. It'd be nice to have a gradual migration path from SDL or graphql-js types. It seems like one option is to generate stub Nexus types for everything and merge it with |
Not sure if will address everything above, but see #148 (comment) for plans related to stitching code first schemas |
I created a repo with a simple example here, hope it helps someone looking to get a feel of how it is done: https://github.com/nayaabkhan/nexus-stitching-example |
Have needed to do this in a real world situation so I'm planning on adding first class support for better support of properly merging in types from an external schema. |
See #983 which will offer better support for interop with an existing schema |
As follow up, contrast, https://github.com/gmac/schema-stitching-handbook/tree/master/subservice-languages/javascript shows a schema stitching way of doing this where you annotate your nexus schema with directives to teach the gateway how to merge your types Key difference is that the nexus schema is a “remote” schema in this example, ie set up as a separate micro service. I am not advocating for this; in my opinion, it is always better when possible to manage your schema as a monolith, except when you can’t. graphql-tools schema stitching is there for you when you can’t. otherwise, you are probably best off with schema merging. graphql tools has utility functions for that, too, but definitely makes sense to take advantage of any local framework capabilities like described above now in nexus |
Check out If have multiple schemas, you'll need to merge into a single schema before consuming using something like Would love to get any feedback on it, I'm planning on using it for a real world use case with Cypress and will continue to refine the API as needed, so consider it potentially experimental/subject to change. |
are there any docs ? I would like to test it out! |
Hi there!
This is a question not an issue or a bug 😄
What is it the best way to do schema stitching with nexus?
The only thing I can think about is using the converter to convert the schemas to code first, but it doesn’t sound ideal and I am not sure if it is possible to use fragment with it.
Thanks for the amazing work!
The text was updated successfully, but these errors were encountered: