I'm running into a situation in which apollo-codegen is not successfully generating typescript code.
For the graphql file (generated/schema.graphql):
type Author {
id: Int!
firstName: String
lastName: String
posts: [Post]
}
type Post {
id: Int!
title: String
author: Author
votes: Int
}
I then run :
$apollo-codegen introspect-schema generated/schema.graphql --output generated/schema.json
this generates a ./generated/schema.json that appears to contain the relevant information (I see information about Author and its properties, and Post and its properties).
I then run
$apollo-codegen generate generated/schema.graphql --schema generated/schema.json --target typescript and get an (effectively) empty output.
// This file was automatically generated and should not be edited.
/* tslint:disable */
/* tslint:enable */
I've tried generating .swift files as well, with similar empty output.
Apollo codegen version is:
"apollo-codegen": "^0.11.2",
Anyone see if what I'm doing wrong?
I'm a collaborator on apollo-codegen. Happy to hear that you're giving it a try!
You're not seeing any output because apollo-codegen generates types based on the GraphQL operations (query, mutation) in your project -- not based solely on the types in your schema.
In our experience, it's very rare that you would send a query for a full GraphQL type. Instead, we have found types based on graphql operations to be the most useful.
For instance, given the types you've provided, you might write a query:
query AuthorQuery {
authors {
firstName,
lastName
}
}
The type that would get generated (and that you'd probably want to use in code that consumes the results of this query, is
type AuthorQuery = {
authors: Array<{
firstName: string,
lastName: string
}>
}
Notice how you would use the AuthorQuery type in your React component (or similar) whereas you wouldn't use an Author type since it would include more fields than you've actually requested.
If you do however have a use-case for a 1:1 type from your graphql schema to typescript, do file an issue on the project itself and I'd be happy to discuss there :)
Related
I'm trying to make a GraphQL query, out of the ordinary, but so far I haven't found anything related.
I would like to leave the where parameter with two optional types, see the example. Is this supported?
input Person {
name: String
email: String
}
input Company {
name: String
domain: String
}
type GetSearch {
name
email
domain
}
extend type Query {
getSearch(where: Person | Company): [GetSearch]
}
This is not possible.
Adding Input Union type to GraphQL was already proposed back in 2018, but it was never added. See this discussion for more details.
what's the difference between schema and documents in Graphql?
schema is like this:
type Query {
fo: String
}
but the document is like:
query SomeQuery {
foo {
bar
}
}
the spec is really confusing https://graphql.github.io/graphql-spec/June2018/#sec-Language.Document
I always use schema but for client-side type generation in graphql-code-generator it needs document file. https://graphql-code-generator.com/docs/getting-started/documents-field
A document is really any string containing valid GraphQL syntax. According to the spec, a document contains one or more definitions, where a definition could be:
an operation definition
query UsersQuery {
users {
id
email
}
}
a fragment definition
fragment UserFragment on User {
id
email
}
a type system definition
type User {
id: ID!
email: String!
}
a type system extension
extend type User {
name: String
}
Operation and fragment definitions are known as executable definitions. Documents sent to a GraphQL service must only contain executable definitions. Type system definitions and extensions are used in describing a schema -- that's why we commonly call them Schema Definition Language (SDL). A schema is a GraphQL service's "collective type system capabilities" -- it's basically a collection of types and directives that represent everything your GraphQL service can do.
A schema may be described using type system definitions, but it's not really accurate to say that the type definitions are the schema because the schema itself also includes the actual field resolution logic as well.
I'm a newbie to Prisma/GraphQL. I'm writing a simple ToDo app and using Apollo Server 2 and Prisma GraphQL for the backend. I want to convert my createdAt field from the data model to something more usable on the front-end, like a UTC date string. My thought was to convert the stored value, which is a DateTime.
My datamodel.prisma has the following for the ToDo type
type ToDo {
id: ID! #id
added: DateTime! #createdAt
body: String!
title: String
user: User!
completed: Boolean! #default(value: false)
}
The added field is a DataTime. But in my schema.js I am listing that field as a String
type ToDo {
id: ID!
title: String,
added: String!
body: String!
user: User!
completed: Boolean!
}
and I convert it in my resolver
ToDo: {
added: async (parent, args) => {
const d = new Date(parent.added)
return d.toUTCString()
}
Is this OK to do? That is, have different types for the same field in the datamodel and the schema? It seems to work OK, but I didn't know if I was opening myself up to trouble down the road, following this technique in other circumstances.
If so, the one thing I was curious about is why accessing parent.added in the ToDo.added resolver doesn't start some kind of 'infinite loop' -- that is, that when you access the parent.added field it doesn't look to the resolver to resolve that field, which accesses the parent.added field, and so on. (I guess it's just clever enough not to do that?)
I've only got limited experience with Prisma, but I understand you can view it as an extra back-end GraphQL layer interfacing between your own GraphQL server and your data (i.e. the database).
Your first model (datamodel.prisma) uses enhanced Prisma syntax and directives to accurately describe your data, and is used by the Prisma layer, while the second model uses standard GraphQL syntax to implement the same object as a valid, standard GraphQL type, and is used by your own back-end.
In effect, if you looked into it, you'd see the DateTime type used by Prisma is actually a String, but is likely used by Prisma to validate date & time formats, etc., so there is no fundamental discrepancy between both models. But even if there was a discrepancy, that would be up to you as you could use resolvers to override the data you get from Prisma before returning it from your own back-end.
In short, what I'm trying to say here is that you're dealing with 2 different GraphQL layers: Prisma and your own. And while Prisma's role is to accurately represent your data as it exists in the database and to provide you with a wide collection of CRUD methods to work with that data, your own layer can (and should) be tailored to your specific needs.
As for your resolver question, parent in this context will hold the object returned by the parent resolver. Imagine you have a getTodo query at the root Query level returning a single item of type ToDo. Let's assume you resolve this to Prisma's default action to retrieve a single ToDo. According to your datamodel.prisma file, this query will resolve into an object that has an added property (which will exist in your DB as the createdAt field, as specified by the #createdAt Prisma directive). So parent.added will hold that value.
What your added resolver does is transform that original piece of data by turning it into an actual Date object and then formatting it into a UTC string, which conforms to your schema.js file where the added field is of type String!.
Now let's say we are using a REST API. I have one endpoint like this: /homeNewsFeed. This API will give us a response like this:
[
{
blockTitle: 'News',
type: 'list',
api: 'http://localhost/news'
},
{
blockTitle: 'Photos',
type: 'gallery',
api: 'http://localhost/gallery'
}
]
Now after getting this we go through the array and call the respective endpoints to load the data. My question is, how to do this in GraphQL? Normally we define the query in the front end code. Without doing that, how to let the server decide what to send?
The main reason to do this is. Imagine we have a mobile app. We need to push new blocks to this news feed without sending an app update. But each item can have their own query.
Normally we define the query in the front end code. Without doing that, how to let the server decide what to send?
Per the spec, a GraphQL execution request must include two things: 1) a schema; and 2) a document containing an operation definition. The operation definition determines what operation (which query or mutation) to execute as well as the format of the response. There are work arounds and exceptions (I'll discuss some below), but, in general, if specifying the shape of the response on the client-side is undesirable or somehow not possible, you should carefully consider whether GraphQL is the right solution for your needs.
That aside, GraphQL lends itself more to a single request, not a series of structured requests like your existing REST API requires. So the response would look more like this:
[
{
title: 'News',
content: [
...
],
},
{
title: 'Photos',
content: [
...
],
}
]
and the corresponding query might look like this:
query HomePageContent {
blocks {
title
content {
# additional fields
}
}
}
Now the question becomes how do differentiate between different kinds of content. This is normally solved by utilizing an interface or union to aggregate multiple types into a single abstract type. The exact structure of your schema will depend on the data you're sending, but here's an example:
interface BlockContentItem {
id: ID!
url: String!
}
type Story implements BlockContentItem {
id: ID!
url: String!
author: String!
title: String!
}
type Image implement BlockContentItem {
id: ID!
url: String!
alt: String!
}
type Block {
title: String!
content: [BlockContentItem!]!
}
type Query {
blocks: [Block!]!
}
You can now query blocks like this:
query HomePageContent {
blocks {
title
content {
# these fields apply to all BlockContentItems
__typename
id
url
# then we use inline fragments to specify type-specific fields
... on Image {
alt
}
... on Story {
author
title
}
}
}
}
Using inline fragments like this ensures type-specific fields are only returned for instances of those types. I included __typename to identify what type a given object is, which may be helpful to the client app (clients like Apollo automatically include this field anyway).
Of course, there is still the issue of what happens when you want to add a new block. If the block's content fits an existing type, no sweat. But what happens when you anticipate you will need a different type in the future, but can't design around that right now?
Typically, that sort of change would require both a schema change on the server and a query change on the client. And in most cases, this will probably be fine because if you're getting data in a different structure, you will have to update your client app anyway. Otherwise, your app won't know how to render the new data structure correctly.
But let's say we want to future-proof our schema anyway. Here's two ways you could go about doing it.
Instead of specifying an interface for content, just utilize a custom JSON scalar. This will effectively throw the response validation out the window, but it will allow you to return whatever you want for the content of a given block.
Abstract out whatever fields might be needed in the future into some kind of value-key type. For example:
.
type MetaItem {
key: String!
value: String!
}
type Block {
title: String!
meta: [MetaItem!]!
# other common fields
}
There's any number of other workarounds, some better than others depending on the kind of data you're working with. But hopefully that gives you some idea how to address the scenario you describe in a GraphQL context.
I've recently started to research the possibility of using GraphQL for requesting dynamic data configurations. The very first thing that jumps out at me is the strongly-typed concept of GraphQL.
Is there a way for GraphQL schemas to handle arrays of mixed type objects? I would greatly appreciate either an explanation or possibly a reference I can read over.
I am currently working with GraphQL with Node.js but a later implementation will be out of a Java Container. All data will be JSON pulled from MongoDB.
You either have to make these disparate types implement the same interface, make your resolvers return unions, or create a custom scalar to hold the dynamic data.
The cleanest approach is the first one: if your resulting objects can be of a limited number of types, define the types so that they implement the same interface, and type your resolvers by the interface. This allows the client to conditionally select sub-fields based on the actual type, and you maintain type safety.
The second approach has similar limitations: you need to know the possible types ahead of time, but they do not have to implement the same interface. It is preferable when the possible values are unrelated to each other and have either/or semantics, like success/failure.
The custom scalar approach is the only one in which you do not need to know the possible types of the result, i.e. the structure of the result can be completely dynamic. Here's an implementation of that approach, known as JSON scalar (i.e. cram any JSON-serializable structure into a scalar value). The big downside of this approach is that it makes sub-selection impossible, as the entire value becomes one big scalar (even though it's a complex object).
Since the question is asking about an array of objects of unknown types, I'll point out that you can, of course, have a list of all the options above.
Examples:
#Interface for any search result
interface SearchResult {
title: String!
url: String!
}
#A specific kind of search result
type Book implements SearchResult {
title: String!
url: String!
author: Author!
isbn: String!
}
type Article implements SearchResult {
title: String!
url: String!
categories: [Category]!
}
type Query {
#Search can return a mix of Books and Articles
search(keyword: String!): [SearchResult!]
}
Or
#No interface this time
type Book {
name: String! #No common fields with Article
author: Author!
publisher: Publisher!
}
type Article {
title: String!
url: String!
categories: [Category]!
}
union SearchResult = Book | Article
type Query {
#Search can return a mix of Books and Articles
search(keyword: String!): [SearchResult!]
}
Or
scalar JSON
type Query {
#Search can return anything at all... All bets are off
search(keyword: String!): [JSON!]
}
If data is completely JSON and you would rather preserve them as is, check out JSON scalar type. Basically,
import { GraphQLObjectType } from 'graphql';
import GraphQLJSON from 'graphql-type-json';
export default new GraphQLObjectType({
name: 'MyType',
fields: {
myField: { type: GraphQLJSON },
},
});
I think it's possible to make a custom/generic type that will fit the need.
So that way it's still a strong typed array but the type will be flexable enough to set what you need.
Here is an example with custom types:
https://github.com/stylesuxx/graphql-custom-types