Quite new to GraphQL and lighthouse library, don't be too harsh.
Since I can't use any models because my data source is an API. I'm trying to create a custom resolver that will pass data to a service who will do everything necessary to retrieve data from the API.
And it constantly returns me this error: "Field \"address\" of type \"[Address!]\" must have a sub selection.",
I believe it's because of the fact I don't use models(just a wild guess)
So far my schema looks like this:
type Query {
address(address: String!): [Address!] #field(resolver: "Address#resolve")
}
type Address {
fullAddress: String!
lowestId: Int!
}
And the mentioned resolver:
public function resolve($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): array
{
return array_map(
function ($address): array {
return [
'fullAddress' => $address->getFullAddress()
];
},
$this->service->getAddress($args['address'])
);
}
Thank you in advance!
The error is not even specific to Lighthouse, any GraphQL server will produce a similar error for what you are trying to do. I assume you are trying a query like this:
{
address(address: "foo")
}
Consider the graph in GraphQL: your server describes available data types and relations between them, forming a graph. Each type could have fields that lead to another type, and that type to another type, and so on. Those references can even form cycles. At some points, the graph may end: types such as scalar values mark the leaves of the graph.
Now, how does a server know which part of the graph you want to see and it should resolve? Through a query: a subselection of a part of that graph. That naturally limits how deep the server must go, it can do the minimal amount of work to return the parts of the graph you queried for.
One rule of queries is that you must always end up at leaf nodes. This is where the error message comes into play: the server sees that Address is not a leaf type and thus asks you to specify how deep you want to traverse the graph. A working query could be:
{
address(address: "foo") {
fullAddress
}
}
Related
I've been looking into GraphQL as a replacement for some REST APIs of mine, and while I think I've wrapped my head around the basics and like most of what I see so far, there's one important feature that seems to be missing.
Let's say I've got a collection of items like this:
{
"id": "aaa",
"name": "Item 1",
...
}
An application needs a map of all those objects, indexed by ID as such:
{
"allItems": {
"aaa": {
"name": "Item 1",
...
},
"aab": {
"name": "Item 2",
...
}
}
}
Every API I've ever written has been able to give results back in a format like this, but I'm struggling to find a way to do it with GraphQL. I keep running across issue 101, but that deals more with unknown schemas. In my case, I know exactly what all the fields are; this is purely about output format. I know I could simply return all the items in an array and reformat it client-side, but that seems like overkill given that it's never been needed in the past, and would make GraphQL feel like a step backwards. I'm not sure if what I'm trying to do is impossible, or I'm just using all the wrong terminology. Should I keep digging, or is GraphQL just not suited to my needs? If this is possible, what might a query look like to retrieve data like this?
I'm currently working with graphql-php on the server, but I'm open to higher-level conceptual responses.
Unfortunately returning objects with arbitrary and dynamic keys like this is not really a first-class citizen in GraphQL. That is not to say you can't achieve the same thing, but in doing so you will lose many of the benefits of GraphQL.
If you are set on returning an object with id keys instead of returning a collection/list of objects containing the ids and then doing the transformation on the client then you can create a special GraphQLScalarType.
const GraphQLAnyObject = new GraphQLScalarType({
name: 'AnyObject',
description: 'Any JSON object. This type bypasses type checking.',
serialize: value => {
return value;
},
parseValue: value => {
return value;
},
parseLiteral: ast => {
if (ast.kind !== Kind.OBJECT) {
throw new GraphQLError("Query error: Can only parse object but got a: " + ast.kind, [ast]);
}
return ast.value;
}
});
The problem with this approach is that since it is a scalar type you cannot supply a selection set to query it. E.G. if you had a type
type MyType implements Node {
id: ID!
myKeyedCollection: AnyObject
}
Then you would only be able to query it like so
query {
getMyType(id: abc) {
myKeyedCollection # note there is no { ... }
}
}
As others have said, I wouldn't recommend this because you are losing a lot of the benefits of GraphQL but it goes to show that GraphQL can still do pretty much anything REST can.
Hope this helps!
I am trying to setup a simple Restfull api using cakephp.
I followed the documentation from the Cakephp site.
But I am encountering the following issue.
I am using Postman plugin to test the Api calls.
I have a table called 'Categories' and in its controller have an action add().
public function add() {
if ($this->request->is('post')) {
$this->Category->create();
if ($this->Category->save($this->data)) {
$message = 'Saved';
}
else {
$message = 'Error';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
}
and in db, I have Category table with schema (id (int 11, PK, A_I), name(varchar(40)), created (datetime), modified(datetime)).
So from postman I send POST requests to http://myProject/categories.json.
From my understanding when i send key:name and value: test, it must save into the database, which works fine. But I must get error when I replace the "key" with something other than name. i.e for exmaple key: name123 and value: test2, But this data too is getting saved in the db without any error except for the name field. i.e it is saving (id:some value, name:"", created:somevalue, modified:someValue)
I dont understand how. Any help will be greatly appreciated.
You will need to provide more info because what you say doesn't make sense. For example what do your posted data look like? Is there any kind of validation in the model? How do you expect a key/value pair to be stored in only one field (specifically name) in the DB?
What happens now is that you tell Cake to save the supplied data ($this->Category->save($this->data)) although you don't check (via cake's validation rules in the model or any other means) that it contains useful arguments and especially Category.name.
As computers will just do what you tell them to do and not what you imply or have in mind, it goes on and sends the calculated created/modified fields to the DB which in turn saves them with the autoincremented ID. Since name doesn't have a UNIQUE or NOT NULL field condition in the DB it is saved as NULL or empty string.
I view my PHP code and JS code as one cohesive unit. I want to begin there interaction by creating an object on the client that looks like the structure below.
By doing this I only have to pass around one object. Sometimes all of the fields are populated, sometimes only 2 or more of the fields are populated.
So by trading off some wasted object properties, I only have to concern myself with passing o_p to different modules with in the MVC on the client and server.
I have functions to convert JavaScript to JSON to PHP.
Is this a valid approach?
Mo.o_p = function (type) {
return {
// current result or data about the data
result : 0,
// send client data
client : {
model : type,
page : {},
args : {}
},
// returned server data
server : {
bookmarks : {},
tweets : {},
smalls : {}
}
};
};
If your model requires these attributes and being empty is an important information for your application, i see no problem there. On the other hand, if your client and server objects are not necessarily connected and handled by different processes, there would be no need to couple them. Just passing some empty attributes should not be a performance problem.
I'm working with a PHP MVC Framework. Works really well. I like the separation of the business layer (model) with the business logic (controller). But i just stumbled upon a problem. Here's the thing:
Suppose i navigate to the following url:
http://localhost/user/showall/
In this case the userController.php is called and within that file there is a method showallAction() which gets executed.
In the showallAction() method i simply do a request to a model which gets all the users for me. Something like this:
public function showallAction()
{
// create userModel object
$users = new userModel();
// get all users and assign the data to a variable which can be accessed in the view
$this->view->users = $users->getAllUsers();
// render views
$this->view->render();
}
So this method gets all the users, assigns the data returned from the userModel to a variable and i can easily work with the returned data in my view. Just a typical MVC thing.
Now here comes the problem.
I also need to create a native iphone variant. Ofcourse the looks will be totally different. So all i actually want to do is to request this url:
http://localhost/user/showall/
And that it just gives me the array (in json format) back. So i can use that for the mobile development.
But this obviously can't be done right now because the showallAction() method assumes that it is for web browser display. It doesn't echo JSON formatted, instead it simply assings the array of users to a variable.
So that means i have to create another method "showallMobileAction()" in order to get the data, but specifically for the mobile device. But this is not an elegant solution. I'm sure that are better ways...
Anyone any idea how can i solve this problem??
In your situation i would modify the routing mechanism.
It would be useful, if you could add extension at the end of URL, which represents the format you expect, like :
http://foo.bar/news/latest >> HTML document
http://foo.bar/news/latest.html >> HTML document
http://foo.bar/news/latest.rss >> you RSS feed
http://foo.bar/news/latest.json >> data in JSON format
It's a simple pattern to recognize. And you can later expand this to add .. dunno .. pdf output, or Atom feeds.
Additionally , two comments :
Model is not a type of objects. Instead it is a layer, containing objects responsible for business logic, and objects responsible for data storage/retrieval.
View should be a full blown object, to which you bind the domain objects (objects responsible for business logic).
You could pass parameters to your url:
/user/showall/json
and get the third URL segment with a custom function or a built-in one. For instance, with CodeIgniter: $this->uri->segment(3).
Some frameworks will pass the additional parameters to your method. Just try this with the URL I wrote above:
public function showallAction()
{
print_r(func_get_args());
}
I'm not familiar with PHP MVC but in general terms I'd use the "accepts" HTML header field to request the response in either "text/html" or "text/json", the controller would check for the accepts type and return the response accordingly.
I'm working with Doctrine2 for the first time, but I think this question is generic enough to not be dependent on a specific ORM.
Should the entities in a Data Mapper pattern be aware - and use - the Mapper?
I have a few specific examples, but they all seem to boil down to the same general question.
If I'm dealing with data from an external source - for example a User has many Messages - and the external source simply provides the latest few entities (like an RSS feed), how can $user->addMessage($message) check for duplicates unless it either is aware of the Mapper, or it 'searches' through the collection (seems like an inefficient thing to do).
Of course a Controller or Transaction Script could check for duplicates before adding the message to the user - but that doesn't seem quite right, and would lead to code duplication.
If I have a large collection - again a User with many Messages - how can the User entity provide limiting and pagination for the collection without actually proxying a Mapper call?
Again, the Controller or Transaction Script or whatever is using the Entity could use the Mapper directly to retrieve a collection of the User's Messages limited by count, date range, or other factors - but that too would lead to code duplication.
Is the answer using Repositories and making the Entity aware of them? (At least for Doctrine2, and whatever analogous concept is used by other ORMs.) At that point the Entity is still relatively decoupled from the Mapper.
Rule #1: Keep your domain model simple and straightforward.
First, don't prematurely optimize something because you think it may be inefficient. Build your domain so that the objects and syntax flow correctly. Keep the interfaces clean: $user->addMessage($message) is clean, precise and unambiguous. Underneath the hood you can utilize any number of patterns/techniques to ensure that integrity is maintained (caching, lookups, etc). You can utilize Services to orchestrate (complex) object dependencies, probably overkill for this but here is a basic sample/idea.
class User
{
public function addMessage(Message $message)
{
// One solution, loop through all messages first, throw error if already exists
$this->messages[] $message;
}
public function getMessage()
{
return $this->messages;
}
}
class MessageService
{
public function addUserMessage(User $user, Message $message)
{
// Ensure unique message for user
// One solution is loop through $user->getMessages() here and make sure unique
// This is more or less the only path to adding a message, so ensure its integrity here before proceeding
// There could also be ACL checks placed here as well
// You could also create functions that provide checks to determine whether certain criteria are met/unmet before proceeding
if ($this->doesUserHaveMessage($user,$message)) {
throw Exception...
}
$user->addMessage($message);
}
// Note, this may not be the correct place for this function to "live"
public function doesUserHaveMessage(User $user, Message $message)
{
// Do a database lookup here
return ($user->hasMessage($message) ? true
}
}
class MessageRepository
{
public function find(/* criteria */)
{
// Use caching here
return $message;
}
}
class MessageFactory
{
public function createMessage($data)
{
//
$message = new Message();
// setters
return $message;
}
}
// Application code
$user = $userRepository->find(/* lookup criteria */);
$message = $messageFactory->create(/* data */);
// Could wrap in try/catch
$messageService->sendUserMessage($user,$message);
Been working with Doctrine2 as well. Your domain entity objects are just that objects...they should not have any idea of where they came from, the domain model just manages them and passes them around to the various functions that manage and manipulate them.
Looking back over, I'm not sure that I completely answered your question. However, I don't think that the entities themselves should have any access to the mappers. Create Services/Repositories/Whatever to operate on the objects and utilize the appropriate techniques in those functions...
Don't overengineer it from the onset either. Keep your domain focused on its goal and refactor when performance is actually an issue.
IMO, an Entity should be oblivious of where it came from, who created it and how to populate its related Entities. In the ORM I use (my own) I am able to define joins between two tables and limiting its results by specifying (in C#) :
SearchCriteria sc = new SearchCriteria();
sc.AddSort("Message.CREATED_DATE","DESC");
sc.MaxRows = 10;
results = Mapper.Read(sc, new User(new Message());
That will result in a join which is limited to 10 items, ordered by date create of message. The Message items will be added to each User. If I write:
results = Mapper.Read(sc, new Message(new User());
the join is reversed.
So, it is possible to make Entities completely unaware of the mapper.
No.
Here's why: trust. You cannot trust data to act on the benefit of the system. You can only trust the system to act on data. This is a fundamental of programming logic.
Let's say something nasty slipped into the data and it was intended for XSS. If a data chunk is performing actions or if it's evaluated, then the XSS code gets blended into things and it will open a security hole.
Let not the left hand know what the right hand doeth! (mostly because you don't want to know)