alexsteinerde / graphql-kit Goto Github PK
View Code? Open in Web Editor NEWEasy setup of a GraphQL server with Vapor. It uses the GraphQL implementation of Graphiti.
License: MIT License
Easy setup of a GraphQL server with Vapor. It uses the GraphQL implementation of Graphiti.
License: MIT License
Hi,
I would need to get access to the Selection Set from the Resolver.
Do you think it is hard to implement ?
The selectionSet is a flat list of the fields that the user requested, for example,
family {
name
child {
name
}
}
Would give the selectionSet ["name", "child/name"]
Thank you
are you sick of me yet? 😄
OK, so now I'm struggling with a Model that has an @OptionalParent
. I spent a while trying to add my own extension to Graphiti.Field
to support the OptionalParentProperty
property wrapper, akin to what you did with the Siblings wrapper. I figured I'd PR it if I could get it to work.
But... I seem to only sorta be able to get it working. I have a Schema type definition that looks sort of like this:
Type("MyModel", MyModel.self) {
// [...]
Field("category", with \.$category) // 👋 <-- here's the @OptionalParent prop
}
The schema won't compile, none of the initializers match an OptionalParent
. Below is my attempt at adding another initializer to handle the relationship:
extension Graphiti.Field where Arguments == NoArguments, Context == Request, ObjectType: Model {
public convenience init<ParentType: Model>(
_ name: FieldKey,
with keyPath: KeyPath<ObjectType, OptionalParentProperty<ObjectType, ParentType>>
) where FieldType == TypeReference<ParentType?> {
self.init(
name.description,
at: {
(type) -> (Request, NoArguments, EventLoopGroup) throws -> EventLoopFuture<ParentType?> in
return { (context: Request, arguments: NoArguments, eventLoop: EventLoopGroup) in
return type[keyPath: keyPath].get(on: context.db)
}
}, as: TypeReference<ParentType?>.self)
}
}
The new initializer allows my Schema to compile. But then, trying to send a query, I get Cannot complete value of unexpected type "CategoryOptional".
Which makes me wonder if I'm just messing up my Schema and resolvers somehow, and if maybe the extension is in fact correct.
The other thing I tried is removing the ?
on the fifth line of the extension and the third-to-last line (both places where it says TypeReference<ParentType?>
. This also allows the schema to compile, but upon querying I get Cannot return null for non-nullable field MyModel.category
.
Any possibility you are able to see at a glance what I've done wrong? Thanks in advance, really appreciate it!
Hi, I'm trying to use enum in my GraphQL schema. But I get the following error.
I did everything just like it says in README.md
My code:
import Fluent
enum FamilyStatus: String, Codable, CaseIterable {
case Free
case inRelationships = "In Relationships"
case inFreeRelationships = "In Free Relationships"
case allAreComplicated = "All Are Complicated"
static func migrate(_ db: Database) async throws {
_ = try await db.enum("family_status")
.case(FamilyStatus.Free.rawValue)
.case("inRelationships")
.case("inFreeRelationships")
.case("allAreComplicated")
.create()
}
}
Enum(FamilyStatus.self)
What is my mistake and how do I fix it?
I'm having trouble resolving the children of a model. I definitely might be doing something super dumb though. What's odd is, in my other vapor/graphql app (yes, I have two now...) I haven't hit this problem, but I use parent/children a decent bit. Anyway, I have models like this:
final class Order: Model, Content {
static let schema = "orders"
@ID(key: .id)
var id: UUID?
@Field(key: "email")
var email: String
@Children(for: \OrderItem.$order)
var items: [OrderItem]
init() {}
}
final class OrderItem: Model, Content {
static let schema = "order_items"
@ID(key: .id)
var id: UUID?
@Parent(key: "order_id")
var order: Order
@Field(key: "title")
var title: String
init() {}
}
The schema looks like this:
let AppSchema = try! Graphiti.Schema<Resolver, Request> {
Scalar(UUID.self)
Type(Order.self) {
Field("id", at: \.id)
Field("email", at: \.email)
Field("items", with: \.$items)
}
Type(OrderItem.self) {
Field("title", at: \.title)
Field("order", with: \.$order)
}
Query {
Field("getOrder", at: Resolver.getOrder) {
Argument("id", at: \.id)
}
}
}
And the resolver looks like this:
final class Resolver {
struct IdentifyEntityArgs: Codable {
let id: UUID
}
func getOrder(
request: Request,
args: IdentifyEntityArgs
) throws -> Future<Order> {
return Order.query(on: request.db)
.with(\.$items)
.filter(\.$id == args.id)
.first()
.unwrap(or: Abort(.notFound))
}
}
But when I send a graphql query like so:
query {
order: getOrder(id: "FBF9C172-E57E-49A8-8806-85F6C923E55A") {
id
items {
title # <-- trying to get one of the children causes the error, without this, it works
}
}
}
I get the error Cannot complete value of unexpected type \"OrderItem\".
I feel like I'm missing something stupid. I think I'm sort of following your examples for users/todos in the readme.
If the code above isn't clear enough, I've created a super minimal reproduction repository using an in-memory sql-lite db. If you want to take a look, you can just clone the repo, then run swift run
and (in another terminal tab) try bash ./test-error.sh
to send the query shown above.
Any plan to upgrade to the latest Graphiti version ?
Numerous improvement and features have been added to Graphiti
It looks like this library in its current form doesn't support GraphQL subscriptions. Is that correct? I'm wanting to implement some new websocket-based functionality in one of my vapor/graphql apps, and I'm trying to get a feel for how much work it would be to keep it purely graphql, or whether I should just build this feature outside of graphql for now. I know the Graphiti library has some support for subscriptions, by subclassing EventStream
from the core GraphQL
library.
have you thought about this at all, or do you have any plans on implementing this? or thoughts on how it would be accomplished? if the path forward seemed not to crazy, i thought maybe i could possibly try an initial implementation, if it was something you were interested in adding support for, or could possibly give me a pointer or two how you'd like to see it implemented in the context of this library.
Hi @alexsteinerde,
How can I return response code other that 200 for specific scenarios (unauthorized etc)?
I've tried adding a custom error middleware but because RoutesBuilder
status is always ok I it never uses it.
Hi,
Swift GraphQL supports resolvers with async throws, in addition to NIO eventLoop.
Do you have any plan to provide support for them too ?
The following code (using a simple UserAuthenticator
example from Vapor's Authentication Docs) does not respect the authenticator:
routes.grouped(UserAuthenticator()).register(graphQLSchema: schema, withResolver: resolver)
I believe the same would hold true for any sort of Vapor middleware, but have only tested the authenticator.
Given this slightly reworked example:
final class User: Model {
...
@Children(for: \.$user)
var todos: [Todo]
...
}
final class Todo: Model {
...
@Parent(key: "user_id")
var user: User
...
}
// Schema types
Type(Todo.self) {
Field("user", with: \.$user)
}
Type(User.self) {
Field("todos", with: \.$todos)
}
I get the following error:
Fatal error: 'try!' expression unexpectedly raised an error: Type "User" was referenced but not defined.
This seems to be because User
needs to be defined in the schema before Todo
for Todo's @Parent
relation, but Todo
also needs to be defined before User
for User's @Children
relation. Essentially we have a circular dependency issue.
When trying to send a date as an argument, I get the error "The data couldn't be read because it isn't in the correct format."
At the same time, the date from the database is decoded normally. If Scalar(Date.self)
is changed to DateScalar(formatter: ISO8601DateFormatter())
, the error becomes: "Argument \"date\" got invalid value 670427457.\nExpected type \"Date\", found 670427457."
. I send the date in ISO 8601 format "2022-03-31T13:50:57Z"
Is there a way to implement cursor-based pagination? 🙂
Hi there. Thanks for this amazing library and all the work you put into it! It's been fantastic to use so far! 🙏
I'm having trouble modeling a many-to-many pivot-table based sibling relationship with vapor+graphql-kit. I'm a bit at a loss of even how to approach the problem. I'm wondering if it's something that is not supported yet, or if possibly I just don't know how to set up the schema and resolvers to make it work.
I see that you extended Graphiti's Field
class here to add keypath-based resolution specifically for children and parent relationships. I'm wondering if something similar would need to be done for sibling relationships. Or is it not possible without some deeper work in the library? Or is it possible by dropping down and working at the Graphiti layer itself somehow?
Have you written an example with some code somewhere of how to do this? (I ask because I relied heavily on your excellent blog posts with the Todo examples when getting up and running).
Any info or pointers would be greatly appreciated. Thanks again!
I started getting an error, before everything was working
{
"error": true,
"reason": "The operation couldn’t be completed. (GraphQLKit.GraphQLResolveError error 0.)"
}
[ INFO ] GET /graphql [request-id: F648C1E3-5FB5-4AAD-B864-955003387E2C]
[ WARNING ] The operation couldn’t be completed. (GraphQLKit.GraphQLResolveError error 0.) [request-id: F648C1E3-5FB5-4AAD-B864-955003387E2C]
I'm building a GraphQL server application in Swift using GraphQL-Kit, Graphiti, Vapor, and Fluent with an SQL database.
Below is a basic example to attempt to illustrate my problem. I have a User
model that maps to a users
table, and a Purchase
model that maps to a purchases
table.
I have a resolver that exposes a function called getUsers
. For brevity I've not included the schema or setup code, for this example assume that the schema replicates roughly the same structure and links to the resolver.
What I would like to do is enrich the response that getUsers
function returns. I have data that comes from another source that I would like to add to the Purchase
response, my idea was to just create a normal property on the Purchase
model called notes
and then in the resolver just use map
to access the instance of each purchase model and assign the values into the notes field, I assumed this would work as the models are classes.
final class User: Model, Codable {
static let schema = "users"
@ID(key: .id)
var id: UUID?
@Children(for: \.$user)
var purchases: [Purchase]
init() {}
init(id: UUID? = nil) {
self.id = id
}
}
final class Purchase: Model, Codable {
static let schema = "purchases"
@ID(key: .id)
var id: UUID?
@Field(key: "name")
var name: String
@Field(key: "price")
var price: Double
@Parent(key: "user_id")
var user: User
var notes: String?
init() {}
init(id: UUID? = nil, name: String, price: Double, userId: UUID, notes: String?) {
self.id = id
self.name = name
self.price = price
self.$user.id = userId
self.notes = notes
}
}
func getUser(
request: Request,
arguments: GetUserArguments
) throws -> EventLoopFuture<User> {
User.find(arguments.id, on: request.db)
.unwrap(or: Abort(.notFound))
.map {
$0.purchases.forEach {
$0.notes = "Some value that comes from somewhere else that I can compute at resolution time"
}
return $0
}
}
When I run my server and perform a query:
query {
users(id: $userId) {
userId
purchases {
name
price
notes
}
}
The notes
field in the graph comes back with a null
.
I'm pretty sure this is just a lack of understanding of how this all works but at the moment I can't for the life of me work out how to enrich the response of a query with data that isn't coming from the db.
For context, I followed the setup and examples in this tutorial series: https://www.kodeco.com/21148796-graphql-tutorial-for-server-side-swift-with-vapor-getting-started#toc-anchor-001
Cross posted on StackOverflow: https://stackoverflow.com/questions/77812573/how-to-enrich-a-response-using-graphql-vapor-and-graphiti
Hi, can you tell me how to get pivot?
I tried this but it gives the error "Unknown kind of type: TodoTag"
Type(Todo.self) {
Field("id", at: \.id)
Field("title", at: \.title)
Field("user", with: \.$user)
Field("tags", with: \.$tags.$pivots)
}
I see that the trunk branch (master) has an up-to-date transitive dependency on Graphiti. I'd like to not have to point to the trunk branch, though. Can you please release what's in master?
Can you provide any input on how to expose @Timestamp
model properties like createdAt
, updatedAt
, on graphql types? I tried just adding Scalar(Date.self)
to my Schema, but I end up just getting numbers (like "createdAt": 648570561.42727697
) in the json output.
After digging around a bit, I think I might need to somehow register a DateScalar
with a Graphiti.DateFormatter
, but I'm struggling to figure out where or how.
I debated opening this on the Swift Graphiti repo, but since it tied in directly with Vapor/Fluent, I thought maybe it might be appropriate here. But feel free to close if you think I should raise it over there instead.
Thanks again!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.